implements ImMsgHisService {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019, xuechao (1521515935@qq.com).
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 | use this file except in compliance with the License. You may obtain a copy of
5 | the License at
6 |
7 | https://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 | License for the specific language governing permissions and limitations under
13 | the License.
14 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/service/ImMsgHisService.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.service;
2 |
3 | import com.baomidou.mybatisplus.extension.service.IService;
4 | import com.chao.cloud.im.dal.entity.ImMsgHis;
5 |
6 | import cn.hutool.core.util.StrUtil;
7 |
8 | /**
9 | * @功能:
10 | * @author: 超君子
11 | * @时间:2019-06-28
12 | * @version 1.0.0
13 | */
14 | public interface ImMsgHisService extends IService {
15 | /**
16 | * 用户类型模板
17 | */
18 | String USER_TEMPLATE = "{}-{}";
19 |
20 | static String fromTo(Integer from, Integer to) {
21 | return StrUtil.format(ImMsgHisService.USER_TEMPLATE, from, to);
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/service/impl/ImGroupUserServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.service.impl;
2 |
3 | import com.chao.cloud.im.dal.entity.ImGroupUser;
4 | import com.chao.cloud.im.dal.mapper.ImGroupUserMapper;
5 | import com.chao.cloud.im.service.ImGroupUserService;
6 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
7 | import org.springframework.stereotype.Service;
8 |
9 | /**
10 | * @功能:
11 | * @author: 超君子
12 | * @时间:2019-06-27
13 | * @version 1.0.0
14 | */
15 | @Service
16 | public class ImGroupUserServiceImpl extends ServiceImpl implements ImGroupUserService {
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/dal/entity/ImGroupUser.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.dal.entity;
2 |
3 | import java.io.Serializable;
4 |
5 | import lombok.Data;
6 | import lombok.EqualsAndHashCode;
7 | import lombok.experimental.Accessors;
8 |
9 | /**
10 | * @功能:
11 | * @author: 超君子
12 | * @时间:2019-06-27
13 | * @version 1.0.0
14 | */
15 | @Data
16 | @EqualsAndHashCode(callSuper = false)
17 | @Accessors(chain = true)
18 | public class ImGroupUser implements Serializable {
19 |
20 | private static final long serialVersionUID = 1L;
21 |
22 | /**
23 | * 用户id
24 | */
25 | private Integer userId;
26 |
27 | /**
28 | * 群id
29 | */
30 | private Integer groupId;
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/websocket/model/ImMsgDTO.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.websocket.model;
2 |
3 | import java.util.Date;
4 |
5 | import lombok.Data;
6 |
7 | @Data
8 | public class ImMsgDTO {
9 |
10 | /**
11 | * group=群id friend=发送者id
12 | */
13 | private Integer id;
14 |
15 | /**
16 | * 用户名
17 | */
18 | private String username;
19 |
20 | /**
21 | * 头像
22 | */
23 | private String avatar;
24 |
25 | /**
26 | * 消息内容
27 | */
28 | private String content;
29 |
30 | /**
31 | * 类型 group friend
32 | */
33 | private String type;
34 | /**
35 | * 时间戳
36 | */
37 | private transient Date createTime;
38 |
39 | public Long getTimestamp() {
40 | return createTime.getTime();
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/websocket/model/WsMsgDTO.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.websocket.model;
2 |
3 | import com.chao.cloud.im.websocket.constant.MsgEnum;
4 |
5 | import lombok.Data;
6 |
7 | /**
8 | * 返回值
9 | * @功能:
10 | * @author: 薛超
11 | * @时间:2019年6月19日
12 | * @version 1.0.0
13 | */
14 | @Data
15 | public class WsMsgDTO {
16 |
17 | /**
18 | * 数据类型
19 | */
20 | private Integer type;
21 |
22 | /**
23 | * 数据内容
24 | */
25 | private Object msg;
26 |
27 | /**
28 | * 提示消息
29 | * @param msg
30 | * @return
31 | */
32 | public static WsMsgDTO buildMsg(MsgEnum type, Object msg) {
33 | WsMsgDTO resp = new WsMsgDTO();
34 | resp.setType(type.type);
35 | resp.setMsg(msg);
36 | return resp;
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/resources/templates/layim/find.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 此为自定义的【查找】页面,因需求不一,所以官方暂不提供该模版结构与样式,实际使用时,可移至该文件到你的项目中,对页面自行把控。
8 |
9 | 文件所在目录(相对于layui.js):/css/modules/layim/html/find.html
10 |
11 |
12 |
13 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/main/resources/public/js/layui-table.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 自定义分页参数
3 | */
4 | var chaoLayuiTable = {
5 | // 请求
6 | request : {
7 | pageName : 'current', // 页码的参数名称,默认:page
8 | limitName : 'size', // 每页数据量的参数名,默认:limit
9 | },
10 | // 响应
11 | response : {
12 | statusName : 'code', // 规定数据状态的字段名称,默认:code
13 | statusCode : 0000, // 规定成功的状态码,默认:0
14 | msgName : 'msg',// 规定状态信息的字段名称,默认:msg
15 | countName : 'count', // 规定数据总数的字段名称,默认:count
16 | dataName : 'data', // 规定数据列表的字段名称,默认:data
17 | },
18 | // 数据解析
19 | parseData : function(res) { // res 即为原始返回的数据
20 | return {
21 | "code" : res.retCode, // 解析接口状态
22 | "msg" : res.retMsg, // 解析提示文本
23 | "count" : res.body.total, // 解析数据长度
24 | "data" : res.body.records
25 | // 解析数据列表
26 | };
27 | }
28 | }
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/ai/config/BAiConfig.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.ai.config;
2 |
3 | import org.springframework.boot.context.properties.ConfigurationProperties;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 |
7 | import com.chao.cloud.im.ai.service.AipUnitService;
8 |
9 | import lombok.Data;
10 |
11 | @Data
12 | @Configuration
13 | @ConfigurationProperties("chao.cloud.im.bai")
14 | public class BAiConfig {
15 |
16 | private String appId;
17 | private String apiKey;
18 | private String secretKey; // 唯一标识
19 |
20 | @Bean
21 | public AipUnitService AipUnit(BAiConfig config) {
22 | AipUnitService aipUnit = new AipUnitService(config.appId, config.apiKey, config.secretKey);
23 | return aipUnit;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/resources/public/css/public.css:
--------------------------------------------------------------------------------
1 | @CHARSET "UTF-8";
2 |
3 | .layui-main-height450 {
4 | height: 450px;
5 | }
6 |
7 | .layui-main-height400 {
8 | height: 400px;
9 | }
10 |
11 | .layui-main-height380 {
12 | height: 380px;
13 | }
14 |
15 | .layui-height20 {
16 | height: 20px;
17 | }
18 |
19 | .layui-height80 {
20 | height: 80px;
21 | }
22 |
23 | .layui-padding5 {
24 | padding: 5px;
25 | }
26 |
27 | .layui-margin5 {
28 | margin: 5px;
29 | }
30 |
31 | .layui-padding10 {
32 | padding: 10px;
33 | }
34 |
35 | .layui-margin10 {
36 | margin: 10px;
37 | }
38 |
39 | .layui-padding20 {
40 | padding: 20px;
41 | }
42 |
43 | .layui-margin20 {
44 | margin: 20px;
45 | }
46 |
47 | body {
48 | background-color: #ABABAB;
49 | color: #333333;
50 | font: 12px/150% 'Microsoft YaHei', Arial, Verdana, "宋体";
51 | font-weight: normal;
52 | }
--------------------------------------------------------------------------------
/license_996.txt:
--------------------------------------------------------------------------------
1 | 版权所有 (c) 2019, xuechao (1521515935@qq.com).
2 |
3 | Apache 2.0 License 同时该协议为补充协议,不允许 996 工作制度企业使用该开源软件
4 |
5 | 反996许可证版本1.0
6 |
7 | 在符合下列条件的情况下,特此免费向任何得到本授权作品的副本(包括源代码、文件和/或相关内容,以下
8 | 统称为“授权作品”)的个人和法人实体授权:被授权个人或法人实体有权以任何目的处置授权作品,包括但
9 | 不限于使用、复制,修改,衍生利用、散布,发布和再许可:
10 |
11 | 1. 个人或法人实体必须在许可作品的每个再散布或衍生副本上包含以上版权声明和本许可证,不得自行修
12 | 改。
13 | 2. 个人或法人实体必须严格遵守与个人实际所在地或个人出生地或归化地、或法人实体注册地或经营地
14 | (以较严格者为准)的司法管辖区所有适用的与劳动和就业相关法律、法规、规则和标准。如果该司法管辖
15 | 区没有此类法律、法规、规章和标准或其法律、法规、规章和标准不可执行,则个人或法人实体必须遵守国
16 | 际劳工标准的核心公约。
17 | 3. 个人或法人不得以任何方式诱导、暗示或强迫其全职或兼职员工或其独立承包人以口头或书面形式同意直接或
18 | 间接限制、削弱或放弃其所拥有的,受相关与劳动和就业有关的法律、法规、规则和标准保护的权利或补救
19 | 措施,无论该等书面或口头协议是否被该司法管辖区的法律所承认,该等个人或法人实体也不得以任何方法
20 | 限制其雇员或独立承包人向版权持有人或监督许可证合规情况的有关当局报告或投诉上述违反许可证的行为
21 | 的权利。
22 |
23 | 该授权作品是"按原样"提供,不做任何明示或暗示的保证,包括但不限于对适销性、特定用途适用性和非侵
24 | 权性的保证。在任何情况下,无论是在合同诉讼、侵权诉讼或其他诉讼中,版权持有人均不承担因本软件或
25 | 本软件的使用或其他交易而产生、引起或与之相关的任何索赔、损害或其他责任。
26 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/websocket/constant/ImConstant.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.websocket.constant;
2 |
3 | /**
4 | * 常量
5 | * @功能:
6 | * @author: 薛超
7 | * @时间: 2019年6月26日
8 | * @version 1.0.0
9 | */
10 | public interface ImConstant {
11 |
12 | String OTHER_INPUT = "对方正在输入... ";
13 |
14 | /**
15 | * 状态
16 | */
17 | interface Status {
18 | /**
19 | * 冻结
20 | */
21 | Integer FREEZE = 0;
22 | /**
23 | * 正常
24 | */
25 | Integer OK = 1;
26 | }
27 |
28 | /**
29 | * 在线状态
30 | */
31 | interface LineStatus {
32 | /**
33 | * 在线
34 | */
35 | String ON = "online";
36 | /**
37 | * 离线
38 | */
39 | String OFF = "offline";
40 | }
41 |
42 | /**
43 | * 在线状态
44 | */
45 | interface Type {
46 | /**
47 | * 群消息
48 | */
49 | String GROUP = "group";
50 | /**
51 | * 朋友消息
52 | */
53 | String FRIEND = "friend";
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/ai/model/BAiResp.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.ai.model;
2 |
3 | import java.util.List;
4 |
5 | import lombok.Data;
6 |
7 | @Data
8 | public class BAiResp {
9 |
10 | private Integer error_code;
11 | private String error_msg;
12 | private Result result;
13 |
14 | @Data
15 | public static class Result {
16 |
17 | private String log_id;
18 | private String interaction_id;
19 | private String version;
20 | private List response_list;
21 |
22 | @Data
23 | public static class Response {
24 | private String msg;
25 | private String origin; // 技能编号
26 | private Schema schema;// 信任度
27 | private List action_list;
28 |
29 | @Data
30 | public static class Schema {
31 | private Double intent_confidence;// 一级准确度
32 |
33 | }
34 |
35 | @Data
36 | public static class Action {
37 | private String say;
38 | private String type;// 返回类型
39 | private Double confidence;// 二级准确度
40 |
41 | }
42 | }
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/controller/RobotController.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.controller;
2 |
3 | import javax.validation.constraints.NotBlank;
4 |
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.validation.annotation.Validated;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RestController;
9 |
10 | import com.chao.cloud.common.entity.Response;
11 | import com.chao.cloud.im.ai.service.TAiRobotService;
12 |
13 | /**
14 | * @功能:机器人聊天
15 | * @author: 超君子
16 | * @时间:2019-07-03
17 | * @version 1.0.0
18 | */
19 | @RequestMapping("/robot")
20 | @RestController
21 | @Validated
22 | public class RobotController {
23 |
24 | @Autowired
25 | private TAiRobotService robotService;
26 |
27 | @RequestMapping("/text")
28 | public Response text(@NotBlank(message = "请输入 text") String text) {
29 | String result = robotService.text(text);
30 | return Response.ok(result);
31 | }
32 |
33 | }
--------------------------------------------------------------------------------
/src/main/resources/static/layui/css/modules/layim/html/find.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 发现
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
此为自定义的【查找】页面,因需求不一,所以官方暂不提供该模版结构与样式,实际使用时,可移至该文件到你的项目中,对页面自行把控。
19 | 文件所在目录(相对于layui.js):/css/modules/layim/html/find.html
20 |
21 |
22 |
23 |
24 |
25 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/controller/BaseController.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.controller;
2 |
3 | import javax.servlet.http.HttpSession;
4 |
5 | import com.chao.cloud.common.exception.BusinessException;
6 | import com.chao.cloud.im.domain.dto.LoginDTO;
7 |
8 | /**
9 | *
10 | * @功能:
11 | * @author: 薛超
12 | * @时间: 2019年6月26日
13 | * @version 1.0.0
14 | */
15 | public abstract class BaseController {
16 |
17 | protected static final String randomCookie = "im-login-cookie:";
18 | protected static final String token_key = "user";
19 |
20 | /**
21 | * 获取当前用户
22 | * @return
23 | */
24 | protected LoginDTO getUser(HttpSession session) {
25 | Object obj = session.getAttribute(token_key);
26 | if (obj instanceof LoginDTO) {
27 | return (LoginDTO) obj;
28 | }
29 | throw new BusinessException("未登录");
30 | }
31 |
32 | /**
33 | * 是否登录
34 | * @return
35 | */
36 | protected boolean isLogin(HttpSession session) {
37 | return session.getAttribute(token_key) instanceof LoginDTO;
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/resources/static/layui/css/modules/code.css:
--------------------------------------------------------------------------------
1 | /** layui-v2.4.5 MIT License By https://www.layui.com */
2 | html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-h3,.layui-code-view{position:relative;font-size:12px}.layui-code-view{display:block;margin:10px 0;padding:0;border:1px solid #e2e2e2;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New}.layui-code-h3{padding:0 10px;height:32px;line-height:32px;border-bottom:1px solid #e2e2e2}.layui-code-h3 a{position:absolute;right:10px;top:0;color:#999}.layui-code-view .layui-code-ol{position:relative;overflow:auto}.layui-code-view .layui-code-ol li{position:relative;margin-left:45px;line-height:20px;padding:0 5px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view pre{margin:0}.layui-code-notepad{border:1px solid #0C0C0C;border-left-color:#3F3F3F;background-color:#0C0C0C;color:#C2BE9E}.layui-code-notepad .layui-code-h3{border-bottom:none}.layui-code-notepad .layui-code-ol li{background-color:#3F3F3F;border-left:none}
--------------------------------------------------------------------------------
/src/main/resources/templates/include.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/ai/constant/AiConstant.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.ai.constant;
2 |
3 | import java.util.List;
4 |
5 | import cn.hutool.core.collection.CollUtil;
6 |
7 | /**
8 | *
9 | * @功能:
10 | * @author: 薛超
11 | * @时间: 2019年7月3日
12 | * @version 1.0.0
13 | */
14 | public interface AiConstant {
15 |
16 | /**
17 | * 机器人白名单
18 | */
19 | List REBOT_ID_LIST = CollUtil.toList(AiConstant.XUE, AiConstant.CHAO);
20 |
21 | int XUE = 3;
22 |
23 | int CHAO = 4;
24 |
25 | int CONFIDENCE_LIMIT = 50;
26 | /**
27 | * 问候编码
28 | */
29 | String GREET_CODE = "65192";
30 |
31 | /**
32 | * 腾讯响应成功返回码
33 | */
34 | Integer TAI_SUCCESS = 0;
35 | /**
36 | * 错误返回值
37 | */
38 | String ERROR_RESULT = "抱歉,无法听懂您的描述,请不要输入特殊字符,包括标点符号";
39 | /**
40 | * 百度api路径
41 | */
42 | String BAI_URL = "https://aip.baidubce.com/rpc/2.0/unit/service/chat";
43 | /**
44 | * 百度请求路径
45 | */
46 | String BAI_REQUEST_TEMPLATE = "static/json/bai_resquest.json";
47 | /**
48 | * 错误的响应状态
49 | */
50 | String BAI_ERROR_TYPE = "failure";
51 | /**
52 | * 要解析的状态
53 | */
54 | String BAI_OK_TYPE = "(clarify|satisfy|chat)";
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/ai/config/TAiConfig.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.ai.config;
2 |
3 | import org.springframework.boot.context.properties.ConfigurationProperties;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 |
7 | import com.chao.cloud.im.ai.service.TAiRobotService;
8 | import com.chao.cloud.im.ai.service.TAipImageClassifyService;
9 |
10 | import lombok.Data;
11 |
12 | @Data
13 | @Configuration
14 | @ConfigurationProperties("chao.cloud.im.tai")
15 | public class TAiConfig {
16 |
17 | private String appId;
18 | private String appKey;
19 | private String session; // 唯一标识
20 |
21 | @Bean
22 | public TAiRobotService tAiRobotService(TAiConfig config) {
23 | TAiRobotService robotService = new TAiRobotService();
24 | robotService.setConfig(config);
25 | return robotService;
26 | }
27 |
28 | @Bean
29 | public TAipImageClassifyService tAipImageClassifyService(TAiConfig config) {
30 | TAipImageClassifyService aipImageClassifyService = new TAipImageClassifyService();
31 | aipImageClassifyService.setConfig(config);
32 | return aipImageClassifyService;
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/websocket/model/WsMsgVO.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.websocket.model;
2 |
3 | import java.util.Date;
4 |
5 | import cn.hutool.core.date.DateUtil;
6 | import lombok.Data;
7 |
8 | /**
9 | * 返回值
10 | * @功能:
11 | * @author: 薛超
12 | * @时间:2019年6月19日
13 | * @version 1.0.0
14 | */
15 | @Data
16 | public class WsMsgVO {
17 |
18 | /**
19 | * 发送人
20 | */
21 | private Integer fromid;
22 |
23 | /**
24 | * 接收人
25 | */
26 | private Integer toid;
27 |
28 | /**
29 | * 接收人名称
30 | */
31 | private String toname;
32 | /**
33 | * 接收人头像
34 | */
35 | private String toavatar;
36 |
37 | /**
38 | * 以下为页面消息展示使用
39 | */
40 | private Boolean system;
41 | /**
42 | * 接收者id
43 | */
44 | private Integer id;
45 | /**
46 | * 发送者名称
47 | */
48 | private String username;
49 | /**
50 | * 内容
51 | */
52 | private String content;
53 | /**
54 | * 头像
55 | */
56 | private String avatar;
57 | /**
58 | * 消息类型 group/friend
59 | */
60 | private String type;
61 | /**
62 | * 毫秒数
63 | */
64 | private Date createTime = DateUtil.date();
65 |
66 | public Long getTimestamp() {
67 | return createTime.getTime();
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/resources/static/layui/lay/modules/code.js:
--------------------------------------------------------------------------------
1 | /** layui-v2.4.5 MIT License By https://www.layui.com */
2 | ;layui.define("jquery",function(e){"use strict";var a=layui.$,l="http://www.layui.com/doc/modules/code.html";e("code",function(e){var t=[];e=e||{},e.elem=a(e.elem||".layui-code"),e.about=!("about"in e)||e.about,e.elem.each(function(){t.push(this)}),layui.each(t.reverse(),function(t,i){var c=a(i),o=c.html();(c.attr("lay-encode")||e.encode)&&(o=o.replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")),c.html(''+o.replace(/[\r\t\n]+/g," ")+" "),c.find(">.layui-code-h3")[0]||c.prepend(''+(c.attr("lay-title")||e.title||"code")+(e.about?'layui.code ':"")+" ");var d=c.find(">.layui-code-ol");c.addClass("layui-box layui-code-view"),(c.attr("lay-skin")||e.skin)&&c.addClass("layui-code-"+(c.attr("lay-skin")||e.skin)),(d.find("li").length/100|0)>0&&d.css("margin-left",(d.find("li").length/100|0)+"px"),(c.attr("lay-height")||e.height)&&d.css("max-height",c.attr("lay-height")||e.height)})})}).addcss("modules/code.css","skincodecss");
--------------------------------------------------------------------------------
/src/main/resources/public/js/config.js:
--------------------------------------------------------------------------------
1 | var $, layer;
2 | layui.use([ 'layer', 'jquery' ], function() {
3 | layer = layui.layer;
4 | $ = layui.$;
5 | });
6 |
7 | /**
8 | * 不刷新当前页
9 | *
10 | * @param url
11 | * @param data
12 | * @returns
13 | */
14 | function ajaxPost(url, data) {
15 | ajaxPostLoad(url, data, false);
16 | }
17 |
18 | /**
19 | * 全局ajax-post提交(刷新当前页)
20 | */
21 | function ajaxPostLoad(url, data, reload) {
22 | $.ajax({
23 | cache : true,
24 | type : "POST",
25 | url : url,
26 | data : data,
27 | // async : false, //同步
28 | layerIndex : -1,
29 | beforeSend : function() {
30 | this.layerIndex = parent.layer.load(0, {
31 | shade : [ 0.1, '#000' ]
32 | });
33 | },
34 | complete : function() {
35 | parent.layer.close(this.layerIndex);
36 | },
37 | success : function(data) {
38 | if (data.retCode == '0000') {
39 | parent.layer.msg("操作成功");
40 | if (reload != false) {// 刷新
41 | parent.reloadCurrTab(); // 当前页面刷新
42 | var index = parent.layer.getFrameIndex(window.name); // 获取窗口索引
43 | parent.layer.close(index);
44 | }
45 | } else {
46 | layer.alert(data.retMsg)
47 | }
48 | },
49 | error : function(request) {
50 | layer.alert("请检查参数");
51 | }
52 | });
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/ChaoCloudImApplication.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.scheduling.annotation.EnableAsync;
7 | import org.springframework.web.socket.server.standard.ServerEndpointExporter;
8 |
9 | import com.chao.cloud.common.extra.ftp.annotation.EnableFtp;
10 | import com.chao.cloud.common.extra.mybatis.annotation.EnableMybatisPlus;
11 | import com.chao.cloud.common.web.annotation.EnableGlobalException;
12 | import com.chao.cloud.common.web.annotation.EnableWeb;
13 |
14 | /**
15 | *
16 | * @功能:im -> web 聊天系统
17 | * @author: 薛超
18 | * @时间: 2019年6月24日
19 | * @version 1.0.0
20 | */
21 | @SpringBootApplication
22 | @EnableWeb // web
23 | @EnableGlobalException // 全局异常处理
24 | @EnableMybatisPlus // 数据库插件-乐观锁
25 | @EnableAsync // 异步调用
26 | @EnableFtp
27 | public class ChaoCloudImApplication {
28 |
29 | public static void main(String[] args) {
30 | SpringApplication.run(ChaoCloudImApplication.class, args);
31 | }
32 |
33 | /**
34 | * 开启WebSocket支持
35 | * @return
36 | */
37 | @Bean
38 | public ServerEndpointExporter serverEndpointExporter() {
39 | return new ServerEndpointExporter();
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/dal/entity/ImDept.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.dal.entity;
2 |
3 | import java.io.Serializable;
4 | import java.util.Date;
5 |
6 | import org.springframework.format.annotation.DateTimeFormat;
7 |
8 | import com.baomidou.mybatisplus.annotation.IdType;
9 | import com.baomidou.mybatisplus.annotation.TableId;
10 | import com.baomidou.mybatisplus.annotation.TableLogic;
11 | import com.baomidou.mybatisplus.annotation.Version;
12 |
13 | import cn.hutool.core.date.DatePattern;
14 | import lombok.Data;
15 | import lombok.EqualsAndHashCode;
16 | import lombok.experimental.Accessors;
17 |
18 | /**
19 | * @功能:
20 | * @author: 超君子
21 | * @时间:2019-06-26
22 | * @version 1.0.0
23 | */
24 | @Data
25 | @EqualsAndHashCode(callSuper = false)
26 | @Accessors(chain = true)
27 | public class ImDept implements Serializable {
28 |
29 | private static final long serialVersionUID = 1L;
30 |
31 | @TableId(value = "id", type = IdType.AUTO)
32 | private Integer id;
33 |
34 | /**
35 | * 部门名称
36 | */
37 | private String name;
38 |
39 | /**
40 | * 是否删除(0未删除1已删除)
41 | */
42 | @TableLogic
43 | private Integer deleted;
44 |
45 | /**
46 | * 乐观锁
47 | */
48 | @Version
49 | private Integer version;
50 |
51 | /**
52 | * 创建日期
53 | */
54 | @DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
55 | private Date createTime;
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/ai/service/TAipImageClassifyService.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.ai.service;
2 |
3 | import org.springframework.beans.factory.InitializingBean;
4 |
5 | import com.chao.cloud.common.exception.BusinessException;
6 | import com.chao.cloud.im.ai.config.TAiConfig;
7 | import com.chao.cloud.im.ai.constant.AiConstant;
8 | import com.chao.cloud.im.ai.model.TAiResp;
9 |
10 | import cn.hutool.core.lang.Assert;
11 | import cn.hutool.json.JSONUtil;
12 | import cn.xsshome.taip.imageclassify.TAipImageClassify;
13 |
14 | /**
15 | *
16 | * @功能:图像识别
17 | * @author: 薛超
18 | * @时间: 2019年7月5日
19 | * @version 1.0.0
20 | */
21 | public class TAipImageClassifyService implements InitializingBean {
22 |
23 | private TAiConfig config;
24 | private TAipImageClassify client;
25 |
26 | public void setConfig(TAiConfig config) {
27 | this.config = config;
28 | }
29 |
30 | public String visionImg(byte[] image) throws Exception {
31 | String result = client.visionImgtotext(image, config.getSession());
32 | // 解析
33 | TAiResp tAiResp = JSONUtil.toBean(result, TAiResp.class);
34 | if (AiConstant.TAI_SUCCESS.equals(tAiResp.getRet())) {
35 | return tAiResp.getData().getText();
36 | }
37 | throw new BusinessException("图片识别失败");
38 | }
39 |
40 | @Override
41 | public void afterPropertiesSet() throws Exception {
42 | Assert.notNull(config);
43 | client = new TAipImageClassify(config.getAppId(), config.getAppKey());
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/dal/entity/ImGroup.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.dal.entity;
2 |
3 | import java.io.Serializable;
4 | import java.util.Date;
5 |
6 | import org.springframework.format.annotation.DateTimeFormat;
7 |
8 | import com.baomidou.mybatisplus.annotation.IdType;
9 | import com.baomidou.mybatisplus.annotation.TableId;
10 | import com.baomidou.mybatisplus.annotation.TableLogic;
11 | import com.baomidou.mybatisplus.annotation.Version;
12 |
13 | import cn.hutool.core.date.DatePattern;
14 | import lombok.Data;
15 | import lombok.EqualsAndHashCode;
16 | import lombok.experimental.Accessors;
17 |
18 | /**
19 | * @功能:
20 | * @author: 超君子
21 | * @时间:2019-06-26
22 | * @version 1.0.0
23 | */
24 | @Data
25 | @EqualsAndHashCode(callSuper = false)
26 | @Accessors(chain = true)
27 | public class ImGroup implements Serializable {
28 |
29 | private static final long serialVersionUID = 1L;
30 |
31 | /**
32 | * 群id
33 | */
34 | @TableId(value = "id", type = IdType.AUTO)
35 | private Integer id;
36 |
37 | /**
38 | * 昵称
39 | */
40 | private String nickName;
41 |
42 | /**
43 | * 头像
44 | */
45 | private String headImg;
46 |
47 | /**
48 | * 是否删除(0未删除1已删除)
49 | */
50 | @TableLogic
51 | private Integer deleted;
52 |
53 | /**
54 | * 乐观锁
55 | */
56 | @Version
57 | private Integer version;
58 |
59 | /**
60 | * 创建日期
61 | */
62 | @DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
63 | private Date createTime;
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/ai/service/TAiRobotService.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.ai.service;
2 |
3 | import org.springframework.beans.factory.InitializingBean;
4 |
5 | import com.chao.cloud.im.ai.config.TAiConfig;
6 | import com.chao.cloud.im.ai.constant.AiConstant;
7 | import com.chao.cloud.im.ai.model.TAiResp;
8 |
9 | import cn.hutool.core.lang.Assert;
10 | import cn.hutool.json.JSONUtil;
11 | import cn.xsshome.taip.nlp.TAipNlp;
12 | import lombok.extern.slf4j.Slf4j;
13 |
14 | /**
15 | *
16 | * @功能:腾讯机器人
17 | * @author: 薛超
18 | * @时间: 2019年7月3日
19 | * @version 1.0.0
20 | */
21 | @Slf4j
22 | public class TAiRobotService implements InitializingBean {
23 |
24 | private TAiConfig config;
25 | private TAipNlp client;
26 |
27 | public void setConfig(TAiConfig config) {
28 | this.config = config;
29 | }
30 |
31 | public String text(String text) {
32 | try {
33 | String result = client.nlpTextchat(config.getSession(), text);// 基础闲聊
34 | // 解析
35 | TAiResp tAiResp = JSONUtil.toBean(result, TAiResp.class);
36 | if (AiConstant.TAI_SUCCESS.equals(tAiResp.getRet())) {
37 | return tAiResp.getData().getAnswer();
38 | }
39 | log.info("[腾讯闲聊解析失败={}]", result);
40 | } catch (Exception e) {
41 | log.error("{}", e);
42 | }
43 | return AiConstant.ERROR_RESULT;
44 | }
45 |
46 | @Override
47 | public void afterPropertiesSet() throws Exception {
48 | Assert.notNull(config);
49 | client = new TAipNlp(config.getAppId(), config.getAppKey());
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/websocket/model/LayimModel.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.websocket.model;
2 |
3 | import java.util.List;
4 |
5 | import cn.hutool.core.util.ReflectUtil;
6 | import lombok.Builder;
7 | import lombok.Data;
8 |
9 | @Data
10 | public class LayimModel {
11 |
12 | private User mine;// 个人信息
13 | private List friend;// 好友列表
14 | private List group;// 群
15 |
16 | public static User userConvert(Object obj) {
17 | User user = new User();
18 | ReflectUtil.setFieldValue(user, "id", ReflectUtil.getFieldValue(obj, "id"));
19 | ReflectUtil.setFieldValue(user, "username", ReflectUtil.getFieldValue(obj, "nickName"));
20 | ReflectUtil.setFieldValue(user, "sign", ReflectUtil.getFieldValue(obj, "sign"));
21 | ReflectUtil.setFieldValue(user, "avatar", ReflectUtil.getFieldValue(obj, "headImg"));
22 | return user;
23 | }
24 |
25 | @Data
26 | public static class User {// 用户
27 | private Integer id;
28 | private String username;// 昵称
29 | private String status;// 状态 若值为offline代表离线,online或者不填为在线
30 | private String sign;// 签名
31 | private String avatar;// 头像
32 | }
33 |
34 | @Data
35 | public static class Friend {// 好友
36 | private Integer id;
37 | private String groupname;// 群组昵称
38 | private List list;// 好友列表
39 | }
40 |
41 | @Data
42 | public static class Group {// 群组
43 | private Integer id;
44 | private String groupname;// 昵称
45 | private String avatar;// 头像
46 | }
47 |
48 | @Data
49 | @Builder
50 | public static class Member {// 群成员
51 | private List list;// 列表
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/dal/entity/ImMsg.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.dal.entity;
2 |
3 | import java.io.Serializable;
4 | import java.util.Date;
5 |
6 | import org.springframework.format.annotation.DateTimeFormat;
7 |
8 | import com.baomidou.mybatisplus.annotation.IdType;
9 | import com.baomidou.mybatisplus.annotation.TableId;
10 | import com.baomidou.mybatisplus.annotation.Version;
11 |
12 | import cn.hutool.core.date.DatePattern;
13 | import lombok.Data;
14 | import lombok.EqualsAndHashCode;
15 | import lombok.experimental.Accessors;
16 |
17 | /**
18 | * @功能:
19 | * @author: 超君子
20 | * @时间:2019-06-28
21 | * @version 1.0.0
22 | */
23 | @Data
24 | @EqualsAndHashCode(callSuper = false)
25 | @Accessors(chain = true)
26 | public class ImMsg implements Serializable {
27 |
28 | private static final long serialVersionUID = 1L;
29 |
30 | @TableId(value = "msg_id", type = IdType.AUTO)
31 | private Integer msgId;
32 |
33 | /**
34 | * 发送人
35 | */
36 | private Integer fromid;
37 |
38 | /**
39 | * 接收人
40 | */
41 | private Integer toid;
42 |
43 | /**
44 | * group=群id friend=发送者id
45 | */
46 | private Integer id;
47 |
48 | /**
49 | * 用户名
50 | */
51 | private String username;
52 |
53 | /**
54 | * 头像
55 | */
56 | private String avatar;
57 |
58 | /**
59 | * 消息内容
60 | */
61 | private String content;
62 |
63 | /**
64 | * 类型 group friend
65 | */
66 | private String type;
67 |
68 | /**
69 | * 乐观锁
70 | */
71 | @Version
72 | private Integer version;
73 |
74 | /**
75 | * 创建日期
76 | */
77 | @DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
78 | private Date createTime;
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/dal/entity/ImMsgHis.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.dal.entity;
2 |
3 | import java.io.Serializable;
4 | import java.util.Date;
5 |
6 | import org.springframework.format.annotation.DateTimeFormat;
7 |
8 | import com.baomidou.mybatisplus.annotation.IdType;
9 | import com.baomidou.mybatisplus.annotation.TableId;
10 | import com.baomidou.mybatisplus.annotation.Version;
11 |
12 | import cn.hutool.core.date.DatePattern;
13 | import lombok.Data;
14 | import lombok.EqualsAndHashCode;
15 | import lombok.experimental.Accessors;
16 |
17 | /**
18 | * @功能:
19 | * @author: 超君子
20 | * @时间:2019-06-28
21 | * @version 1.0.0
22 | */
23 | @Data
24 | @EqualsAndHashCode(callSuper = false)
25 | @Accessors(chain = true)
26 | public class ImMsgHis implements Serializable {
27 |
28 | private static final long serialVersionUID = 1L;
29 |
30 | @TableId(value = "msg_id", type = IdType.AUTO)
31 | private Integer msgId;
32 |
33 | /**
34 | * 发送人->接收人 如 1-2
35 | */
36 | private String fromTo;
37 |
38 | /**
39 | * group=群id friend=发送者id
40 | */
41 | private Integer id;
42 |
43 | /**
44 | * 用户名
45 | */
46 | private String username;
47 |
48 | /**
49 | * 头像
50 | */
51 | private String avatar;
52 |
53 | /**
54 | * 消息内容
55 | */
56 | private String content;
57 |
58 | /**
59 | * 类型 group friend
60 | */
61 | private String type;
62 |
63 | /**
64 | * 乐观锁
65 | */
66 | @Version
67 | private Integer version;
68 |
69 | /**
70 | * 创建日期
71 | */
72 | @DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
73 | private Date createTime;
74 |
75 | public Long getTimestamp() {
76 | return createTime.getTime();
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/resources/static/layui/lay/modules/laytpl.js:
--------------------------------------------------------------------------------
1 | /** layui-v2.4.5 MIT License By https://www.layui.com */
2 | ;layui.define(function(e){"use strict";var r={open:"{{",close:"}}"},c={exp:function(e){return new RegExp(e,"g")},query:function(e,c,t){var o=["#([\\s\\S])+?","([^{#}])*?"][e||0];return n((c||"")+r.open+o+r.close+(t||""))},escape:function(e){return String(e||"").replace(/&(?!#?[a-zA-Z0-9]+;)/g,"&").replace(//g,">").replace(/'/g,"'").replace(/"/g,""")},error:function(e,r){var c="Laytpl Error:";return"object"==typeof console&&console.error(c+e+"\n"+(r||"")),c+e}},n=c.exp,t=function(e){this.tpl=e};t.pt=t.prototype,window.errors=0,t.pt.parse=function(e,t){var o=this,p=e,a=n("^"+r.open+"#",""),l=n(r.close+"$","");e=e.replace(/\s+|\r|\t|\n/g," ").replace(n(r.open+"#"),r.open+"# ").replace(n(r.close+"}"),"} "+r.close).replace(/\\/g,"\\\\").replace(n(r.open+"!(.+?)!"+r.close),function(e){return e=e.replace(n("^"+r.open+"!"),"").replace(n("!"+r.close),"").replace(n(r.open+"|"+r.close),function(e){return e.replace(/(.)/g,"\\$1")})}).replace(/(?="|')/g,"\\").replace(c.query(),function(e){return e=e.replace(a,"").replace(l,""),'";'+e.replace(/\\/g,"")+';view+="'}).replace(c.query(1),function(e){var c='"+(';return e.replace(/\s/g,"")===r.open+r.close?"":(e=e.replace(n(r.open+"|"+r.close),""),/^=/.test(e)&&(e=e.replace(/^=/,""),c='"+_escape_('),c+e.replace(/\\/g,"")+')+"')}),e='"use strict";var view = "'+e+'";return view;';try{return o.cache=e=new Function("d, _escape_",e),e(t,c.escape)}catch(u){return delete o.cache,c.error(u,p)}},t.pt.render=function(e,r){var n,t=this;return e?(n=t.cache?t.cache(e,c.escape):t.parse(t.tpl,e),r?void r(n):n):c.error("no data")};var o=function(e){return"string"!=typeof e?c.error("Template not found"):new t(e)};o.config=function(e){e=e||{};for(var c in e)r[c]=e[c]},o.v="1.2.0",e("laytpl",o)});
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/dal/entity/ImUser.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.dal.entity;
2 |
3 | import java.io.Serializable;
4 | import java.util.Date;
5 |
6 | import org.springframework.format.annotation.DateTimeFormat;
7 |
8 | import com.baomidou.mybatisplus.annotation.IdType;
9 | import com.baomidou.mybatisplus.annotation.TableId;
10 | import com.baomidou.mybatisplus.annotation.TableLogic;
11 | import com.baomidou.mybatisplus.annotation.Version;
12 |
13 | import cn.hutool.core.date.DatePattern;
14 | import lombok.Data;
15 | import lombok.EqualsAndHashCode;
16 | import lombok.experimental.Accessors;
17 |
18 | /**
19 | * @功能:
20 | * @author: 超君子
21 | * @时间:2019-06-26
22 | * @version 1.0.0
23 | */
24 | @Data
25 | @EqualsAndHashCode(callSuper = false)
26 | @Accessors(chain = true)
27 | public class ImUser implements Serializable {
28 |
29 | private static final long serialVersionUID = 1L;
30 |
31 | @TableId(value = "id", type = IdType.AUTO)
32 | private Integer id;
33 |
34 | /**
35 | * 用户名
36 | */
37 | private String userName;
38 |
39 | /**
40 | * 密码
41 | */
42 | private String password;
43 |
44 | /**
45 | * 昵称
46 | */
47 | private String nickName;
48 |
49 | /**
50 | * 签名
51 | */
52 | private String sign;
53 |
54 | /**
55 | * 头像
56 | */
57 | private String headImg;
58 |
59 | /**
60 | * 部门id
61 | */
62 | private Integer deptId;
63 |
64 | /**
65 | * 部门名称
66 | */
67 | private String deptName;
68 |
69 | /**
70 | * 状态 0.冻结 1.正常
71 | */
72 | private Integer status;
73 |
74 | /**
75 | * 是否删除(0未删除1已删除)
76 | */
77 | @TableLogic
78 | private Integer deleted;
79 |
80 | /**
81 | * 乐观锁
82 | */
83 | @Version
84 | private Integer version;
85 |
86 | /**
87 | * 创建日期
88 | */
89 | @DateTimeFormat(pattern = DatePattern.NORM_DATETIME_PATTERN)
90 | private Date createTime;
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/src/main/resources/application.yaml:
--------------------------------------------------------------------------------
1 | #日志
2 | logging.config: classpath:chao-logback.xml
3 | log.path: /logs/chao/${spring.application.name}
4 | log.maxHistory: 30
5 |
6 | server:
7 | port: 9001
8 | tomcat.uri-encoding: utf-8
9 | servlet:
session:
timeout: 1800
10 | #共享文件(功能类型)
11 | spring:
12 | application:
13 | name: chao-im
14 | aop:
15 | auto: true
16 | output:
17 | ansi:
18 | enabled: always
19 | jackson:
20 | date-format: yyyy-MM-dd HH:mm:ss
21 | time-zone: GMT+8
22 | datasource:
23 | url: jdbc:mysql://127.0.0.1:3306/im?useSSL=false&useUnicode=true&characterEncoding=UTF8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
24 | driver-class-name: com.mysql.cj.jdbc.Driver
25 | username: root
26 | password: 123456
27 | type: com.alibaba.druid.pool.DruidDataSource
28 | druid:
29 | validation-query: SELECT 1
30 | test-on-borrow: true
31 | connection-init-sqls: SET NAMES utf8mb4;
32 | filter:
33 | wall:
34 | enabled: false
35 | thymeleaf:
36 | mode: LEGACYHTML5
37 | cache: false
38 | resources: #这里必须使用字符串 后面的 / 必须有
39 | static-locations: file:/softrun/tech/dcdy/web/chao/resource/im/
40 | servlet: #文件上传
41 | multipart:
42 | max-file-size: 5MB
43 | max-request-size: 5MB
44 |
45 | mybatis-plus:
46 | configuration:
47 | map-underscore-to-camel-case: true
48 | log-impl: com.chao.cloud.common.extra.mybatis.log.Slf4jLogImpl
49 |
50 | #默认使用本地数据源-代码自动生成
51 | chao:
52 | cloud:
53 | im:
54 | tai: #腾讯
55 | app-id:
56 | app-key:
57 | session:
58 | bai:
59 | app-id:
60 | api-key:
61 | secret-key:
62 | ftp:
63 | config:
64 | local: true #使用本地硬盘
65 | prefix: /softrun/tech/dcdy/web/chao/resource/im
66 | path: ${chao.cloud.ftp.config.prefix}/img
67 |
--------------------------------------------------------------------------------
/src/main/resources/static/layui/lay/modules/flow.js:
--------------------------------------------------------------------------------
1 | /** layui-v2.4.5 MIT License By https://www.layui.com */
2 | ;layui.define("jquery",function(e){"use strict";var l=layui.$,o=function(e){},t=' ';o.prototype.load=function(e){var o,i,n,r,a=this,c=0;e=e||{};var f=l(e.elem);if(f[0]){var m=l(e.scrollElem||document),u=e.mb||50,s=!("isAuto"in e)||e.isAuto,v=e.end||"没有更多了",y=e.scrollElem&&e.scrollElem!==document,d="加载更多 ",h=l('");f.find(".layui-flow-more")[0]||f.append(h);var p=function(e,t){e=l(e),h.before(e),t=0==t||null,t?h.html(v):h.find("a").html(d),i=t,o=null,n&&n()},g=function(){o=!0,h.find("a").html(t),"function"==typeof e.done&&e.done(++c,p)};if(g(),h.find("a").on("click",function(){l(this);i||o||g()}),e.isLazyimg)var n=a.lazyimg({elem:e.elem+" img",scrollElem:e.scrollElem});return s?(m.on("scroll",function(){var e=l(this),t=e.scrollTop();r&&clearTimeout(r),i||(r=setTimeout(function(){var i=y?e.height():l(window).height(),n=y?e.prop("scrollHeight"):document.documentElement.scrollHeight;n-t-i<=u&&(o||g())},100))}),a):a}},o.prototype.lazyimg=function(e){var o,t=this,i=0;e=e||{};var n=l(e.scrollElem||document),r=e.elem||"img",a=e.scrollElem&&e.scrollElem!==document,c=function(e,l){var o=n.scrollTop(),r=o+l,c=a?function(){return e.offset().top-n.offset().top+o}():e.offset().top;if(c>=o&&c<=r&&!e.attr("src")){var m=e.attr("lay-src");layui.img(m,function(){var l=t.lazyimg.elem.eq(i);e.attr("src",m).removeAttr("lay-src"),l[0]&&f(l),i++})}},f=function(e,o){var f=a?(o||n).height():l(window).height(),m=n.scrollTop(),u=m+f;if(t.lazyimg.elem=l(r),e)c(e,f);else for(var s=0;su)break}};if(f(),!o){var m;n.on("scroll",function(){var e=l(this);m&&clearTimeout(m),m=setTimeout(function(){f(null,e)},50)}),o=!0}return f},e("flow",new o)});
--------------------------------------------------------------------------------
/src/main/resources/public/js/ws-im-chat.js:
--------------------------------------------------------------------------------
1 | var $, layer, connSingleChat, layim, ws;
2 | layui.use([ 'layer', 'jquery' ], function() {
3 | layer = layui.layer;
4 | $ = layui.$;
5 |
6 | $("#off_line").click(function(){
7 | // 退出
8 | layer.msg('您确定要下线吗?', {
9 | time: 0 // 不自动关闭
10 | ,btn: ['确定', '取消']
11 | ,yes: function(index){
12 | layer.close(index);
13 | ws.close();
14 | window.top.location ="/logout";
15 | }
16 | });
17 | });
18 |
19 | // 连接websocket
20 | connSingleChat = function() {
21 | var url = document.location.protocol == "https:" ? "wss://" : "ws://" + window.location.host//
22 | + "/im/chat/" + $("#userId").val();
23 | // 连接
24 |
25 | // 连接服务器
26 | ws = new ReconnectingWebSocket(url);
27 | // 刚刚打开连接
28 | ws.onopen = function(evt) {
29 | console.log("Connection open ...");
30 | };
31 |
32 | // 接收消息的情况
33 | ws.onmessage = function(evt) {
34 | var r = JSON.parse(evt.data);
35 | console.log(r);
36 | switch (r.type) {
37 | case 0:// 关闭-已经登录
38 | layer.msg(r.msg);
39 | setTimeout(function() {
40 | // 跳转登录页
41 | window.top.location = "/logout";
42 | }, 3000);
43 | break;
44 | case 1:// 开始连接
45 | break;
46 | case 2:// 提示
47 | break;
48 | case 3:// 上线
49 | layim.setFriendStatus(r.msg, 'online');
50 | break;
51 | case 4:// 下线
52 | layim.setFriendStatus(r.msg, 'offline');
53 | break;
54 | case 5:// 聊天->追加消息
55 | layim.setChatStatus('');// 清除对方正在输入
56 | layim.getMessage(r.msg);
57 | break;
58 | case 6:// 离线消息
59 | $.each(r.msg,function(i,item){
60 | layim.getMessage(item);
61 | });
62 | break;
63 | case 7:// 对方正在输入
64 | layim.setChatStatus(r.msg);
65 | break;
66 |
67 | }
68 |
69 | };
70 | // 关闭连接的情况
71 | ws.onclose = function(evt) {
72 | console.log("连接关闭");
73 | };
74 |
75 | // 连接失败的情况
76 | ws.onerror = function(event) {
77 | console.log("连接失败");
78 | };
79 |
80 | }
81 | });
82 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/controller/MsgHisController.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.controller;
2 |
3 | import java.util.Set;
4 |
5 | import javax.servlet.http.HttpSession;
6 | import javax.validation.Valid;
7 |
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.validation.annotation.Validated;
10 | import org.springframework.web.bind.annotation.RequestMapping;
11 | import org.springframework.web.bind.annotation.RestController;
12 |
13 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
14 | import com.baomidou.mybatisplus.core.metadata.IPage;
15 | import com.baomidou.mybatisplus.core.toolkit.Wrappers;
16 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
17 | import com.chao.cloud.common.entity.Response;
18 | import com.chao.cloud.im.dal.entity.ImMsgHis;
19 | import com.chao.cloud.im.domain.dto.LoginDTO;
20 | import com.chao.cloud.im.domain.vo.MsgHisVO;
21 | import com.chao.cloud.im.service.ImMsgHisService;
22 |
23 | import cn.hutool.core.collection.CollUtil;
24 |
25 | /**
26 | * @功能:历史记录
27 | * @author: 超君子
28 | * @时间:2019-06-28
29 | * @version 1.0.0
30 | */
31 | @RequestMapping("/im/msg_his")
32 | @RestController
33 | @Validated
34 | public class MsgHisController extends BaseController {
35 |
36 | @Autowired
37 | private ImMsgHisService imMsgHisService;
38 |
39 | @RequestMapping("/list")
40 | public Response> list(Page page, // 分页
41 | HttpSession session, @Valid MsgHisVO vo) {
42 | // 获取当前用户
43 | LoginDTO user = getUser(session);
44 | // 去重
45 | Set fromTo = CollUtil.newHashSet(ImMsgHisService.fromTo(user.getId(), vo.getId()),
46 | ImMsgHisService.fromTo(vo.getId(), user.getId()));
47 | // 构造条件
48 | LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery();
49 | queryWrapper.in(ImMsgHis::getFromTo, fromTo);
50 | queryWrapper.eq(ImMsgHis::getType, vo.getType());
51 | return Response.ok(imMsgHisService.page(page, queryWrapper));
52 | }
53 |
54 | }
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/service/impl/ImMsgServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.service.impl;
2 |
3 | import java.util.List;
4 | import java.util.Set;
5 | import java.util.stream.Collectors;
6 |
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.scheduling.annotation.Async;
9 | import org.springframework.stereotype.Service;
10 | import org.springframework.transaction.annotation.Transactional;
11 |
12 | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
13 | import com.chao.cloud.im.dal.entity.ImMsg;
14 | import com.chao.cloud.im.dal.entity.ImMsgHis;
15 | import com.chao.cloud.im.dal.mapper.ImMsgMapper;
16 | import com.chao.cloud.im.service.ImMsgHisService;
17 | import com.chao.cloud.im.service.ImMsgService;
18 | import com.chao.cloud.im.websocket.LayimChatWebSocket;
19 | import com.chao.cloud.im.websocket.model.WsMsgVO;
20 |
21 | import cn.hutool.core.bean.BeanUtil;
22 | import cn.hutool.core.collection.CollUtil;
23 | import cn.hutool.core.util.BooleanUtil;
24 |
25 | /**
26 | * @功能:
27 | * @author: 超君子
28 | * @时间:2019-06-26
29 | * @version 1.0.0
30 | */
31 | @Service
32 | public class ImMsgServiceImpl extends ServiceImpl implements ImMsgService {
33 |
34 | @Autowired
35 | private LayimChatWebSocket layimChatWebSocket;
36 | @Autowired
37 | private ImMsgHisService imMsgHisService;
38 |
39 | /**
40 | * 异步消息推送
41 | */
42 | @Async
43 | @Override
44 | @Transactional
45 | public void saveOffLineMsg(WsMsgVO vo, List receiveIds) {
46 | boolean isSystem = BooleanUtil.isTrue(vo.getSystem());
47 | if (!isSystem) {// 非系统消息
48 | Set key = layimChatWebSocket.onlineKey();
49 | // 遍历接收者->改变to的值
50 | List list = receiveIds.stream().filter(k -> !key.contains(k))
51 | .map(BeanUtil.toBean(vo, ImMsg.class)::setToid).collect(Collectors.toList());
52 | // 批量添加
53 | if (CollUtil.isNotEmpty(list)) {
54 | this.saveBatch(list, list.size());
55 | }
56 | // 增加历史记录
57 | ImMsgHis msgHis = BeanUtil.toBean(vo, ImMsgHis.class)
58 | .setFromTo(ImMsgHisService.fromTo(vo.getFromid(), vo.getToid()));
59 | imMsgHisService.save(msgHis);
60 | }
61 |
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.github.chaojunzi
8 | chao-cloud-parent
9 | 1.0.8
10 |
11 | chao-cloud-im
12 | jar
13 |
14 | 4.3.5
15 |
16 |
17 |
18 |
19 | cn.xsshome
20 | taip
21 | ${taip.version}
22 |
23 |
24 | com.github.chaojunzi
25 | chao-cloud-common-config
26 |
27 |
28 | org.springframework.boot
29 | spring-boot-starter-web
30 |
31 |
32 | org.springframework.boot
33 | spring-boot-starter-thymeleaf
34 |
35 |
36 | org.springframework.boot
37 | spring-boot-starter-websocket
38 |
39 |
40 |
41 | com.alibaba
42 | druid-spring-boot-starter
43 |
44 |
45 | com.baomidou
46 | mybatis-plus-boot-starter
47 |
48 |
49 |
50 | chao-im
51 |
52 |
53 | org.springframework.boot
54 | spring-boot-maven-plugin
55 |
56 |
57 |
58 |
59 | ${project.basedir}/src/main/resources
60 | true
61 |
62 | */**
63 |
64 |
65 |
66 | ${project.basedir}/src/main/resources
67 | false
68 |
69 | */**
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/src/main/resources/static/layui/css/modules/layim/html/getmsg.json:
--------------------------------------------------------------------------------
1 | {
2 | "code": 0,
3 | "pages": 1,
4 | "data": [
5 | {
6 | "id": 76,
7 | "content": "申请添加你为好友",
8 | "uid": 168,
9 | "from": 166488,
10 | "from_group": 0,
11 | "type": 1,
12 | "remark": "有问题要问",
13 | "href": null,
14 | "read": 1,
15 | "time": "刚刚",
16 | "user": {
17 | "id": 166488,
18 | "avatar": "http://q.qlogo.cn/qqapp/101235792/B704597964F9BD0DB648292D1B09F7E8/100",
19 | "username": "李彦宏",
20 | "sign": null
21 | }
22 | },
23 | {
24 | "id": 75,
25 | "content": "申请添加你为好友",
26 | "uid": 168,
27 | "from": 347592,
28 | "from_group": 0,
29 | "type": 1,
30 | "remark": "你好啊!",
31 | "href": null,
32 | "read": 1,
33 | "time": "刚刚",
34 | "user": {
35 | "id": 347592,
36 | "avatar": "http://q.qlogo.cn/qqapp/101235792/B78751375E0531675B1272AD994BA875/100",
37 | "username": "麻花疼",
38 | "sign": null
39 | }
40 | },
41 | {
42 | "id": 62,
43 | "content": "雷军 拒绝了你的好友申请",
44 | "uid": 168,
45 | "from": null,
46 | "from_group": null,
47 | "type": 1,
48 | "remark": null,
49 | "href": null,
50 | "read": 1,
51 | "time": "10天前",
52 | "user": {
53 | "id": null
54 | }
55 | },
56 | {
57 | "id": 60,
58 | "content": "马小云 已经同意你的好友申请",
59 | "uid": 168,
60 | "from": null,
61 | "from_group": null,
62 | "type": 1,
63 | "remark": null,
64 | "href": null,
65 | "read": 1,
66 | "time": "10天前",
67 | "user": {
68 | "id": null
69 | }
70 | },
71 | {
72 | "id": 61,
73 | "content": "贤心 已经同意你的好友申请",
74 | "uid": 168,
75 | "from": null,
76 | "from_group": null,
77 | "type": 1,
78 | "remark": null,
79 | "href": null,
80 | "read": 1,
81 | "time": "10天前",
82 | "user": {
83 | "id": null
84 | }
85 | }
86 | ]
87 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | # chao-cloud-im
12 | spring-boot为基础,websocket+mybatis-plus+layui+layim+腾讯闲聊机器人+百度聊天机器人
13 |
14 | ## 集成
15 |
16 | - [腾讯ai@申请](https://ai.qq.com/console/home)
17 | - [百度ai@申请](http://ai.baidu.com/)
18 |
19 | ```
20 | //1.导入sql
21 | resource
22 | │
23 | └─sql/mysql-im.sql
24 | //2.修改 bootstrap.yaml
25 | chao:
26 | cloud:
27 | im:
28 | tai: #腾讯AI-机器人
29 | app-id:
30 | app-key:
31 | session:
32 | bai: #百度Ai-机器人
33 | app-id:
34 | api-key:
35 | secret-key:
36 | //3.引入layim.js
37 | resource
38 | │
39 | └─static
40 | └─layui
41 | └─lay
42 | └─modules/layim.js
43 | ```
44 |
45 | ## 注意
46 |
47 | - layim是收费UI,请大家自行前往官网授权
48 | - 项目源代码中不包含layim.js包
49 | - 拿去玩吧,禁止不授权使用,[贤心](https://github.com/sentsin)会找你的
50 | - [LAYIM@官网](http://layim.layui.com/)
51 |
52 | ## 效果图展示
53 |
54 | 
55 | 
56 | 
57 | 
58 | 
59 | 
60 | 
61 | 
62 | 
63 |
64 | ## 版权
65 |
66 | ### Apache License Version 2.0
67 |
68 | - 如不特殊注明,所有模块都以此协议授权使用。
69 | - 任何使用了chao-cloud-im的全部或部分功能的项目、产品或文章等形式的成果必须显式注明chao-cloud-im。
70 |
71 | ### NPL (The 996 Prohibited License)
72 |
73 | - 不允许 996 工作制度企业使用该开源软件
74 |
75 | ### LAYIM
76 |
77 | - LayIM 受国家计算机软件著作权保护(登记号:2018SR064263)
78 | - 未经官网正规渠道授权擅自公开产品源文件、和直接对产品二次出售的,将追究法律责任
79 |
80 | ### 鸣谢
81 | - [java-sdk@百度AI](https://github.com/Baidu-AIP/java-sdk)
82 | - [xshuai@腾讯AI](https://gitee.com/xshuai/taip)
83 | - [hutool-超级工具类](https://github.com/looly/hutool)
84 | - [lombok](https://github.com/rzwitserloot/lombok)
85 | - [layim-js](https://gitee.com/pctao/instant_chat_tools)
86 | - [mybatis-plus](https://github.com/baomidou/mybatis-plus)
87 | - [layui](https://github.com/sentsin/layui/)
88 | - [......](https://github.com/)
89 |
90 | 感谢诸位用户的关注和使用,chao-cloud-im并不完善,未来还恳求各位开源爱好者多多关照,提出宝贵意见。
91 |
92 | 作者 [@chaojunzi 1521515935@qq.com]
93 |
94 | 2019年8月15日
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/controller/MsgController.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.controller;
2 |
3 | import javax.validation.constraints.NotNull;
4 | import javax.validation.constraints.Size;
5 |
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.validation.annotation.Validated;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.RequestParam;
10 | import org.springframework.web.bind.annotation.RestController;
11 |
12 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
13 | import com.baomidou.mybatisplus.core.metadata.IPage;
14 | import com.baomidou.mybatisplus.core.toolkit.Wrappers;
15 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
16 | import com.chao.cloud.common.entity.Response;
17 | import com.chao.cloud.im.dal.entity.ImMsg;
18 | import com.chao.cloud.im.service.ImMsgService;
19 |
20 | import cn.hutool.core.collection.CollUtil;
21 |
22 | /**
23 | * @功能:
24 | * @author: 超君子
25 | * @时间:2019-06-26
26 | * @version 1.0.0
27 | */
28 | @RequestMapping("/im/msg")
29 | @RestController
30 | @Validated
31 | public class MsgController {
32 |
33 | @Autowired
34 | private ImMsgService imMsgService;
35 |
36 | @RequestMapping("/list")
37 | public Response> list(Page page //
38 | ) { // 分页
39 | LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery();
40 | return Response.ok(imMsgService.page(page, queryWrapper));
41 | }
42 |
43 | /**
44 | * 保存
45 | */
46 | @RequestMapping("/save")
47 | public Response save(ImMsg imMsg) {
48 | boolean result = imMsgService.save(imMsg);
49 | return result ? Response.ok() : Response.error();
50 | }
51 |
52 | /**
53 | * 修改
54 | */
55 | @RequestMapping("/update")
56 | public Response update(ImMsg imMsg) {
57 | boolean result = imMsgService.updateById(imMsg);
58 | return result ? Response.ok() : Response.error();
59 | }
60 |
61 | /**
62 | * 删除
63 | */
64 | @RequestMapping("/remove")
65 | public Response remove(@NotNull(message = "id 不能为空") Integer id) {
66 | boolean result = imMsgService.removeById(id);
67 | return result ? Response.ok() : Response.error();
68 | }
69 |
70 | /**
71 | * 批量删除
72 | */
73 | @RequestMapping("/batchRemove")
74 | public Response batchRemove(
75 | @NotNull(message = "不能为空") @Size(min = 1, message = "请至少选择一个") @RequestParam("ids[]") Integer[] ids) {
76 | boolean result = imMsgService.removeByIds(CollUtil.toList(ids));
77 | return result ? Response.ok() : Response.error();
78 | }
79 |
80 | }
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/controller/UserController.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.controller;
2 |
3 | import javax.validation.constraints.NotNull;
4 | import javax.validation.constraints.Size;
5 |
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.validation.annotation.Validated;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.RequestParam;
10 | import org.springframework.web.bind.annotation.RestController;
11 |
12 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
13 | import com.baomidou.mybatisplus.core.metadata.IPage;
14 | import com.baomidou.mybatisplus.core.toolkit.Wrappers;
15 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
16 | import com.chao.cloud.common.entity.Response;
17 | import com.chao.cloud.im.dal.entity.ImUser;
18 | import com.chao.cloud.im.service.ImUserService;
19 |
20 | import cn.hutool.core.collection.CollUtil;
21 |
22 | /**
23 | * @功能:
24 | * @author: 超君子
25 | * @时间:2019-06-26
26 | * @version 1.0.0
27 | */
28 | @RequestMapping("/im/user")
29 | @RestController
30 | @Validated
31 | public class UserController {
32 |
33 | @Autowired
34 | private ImUserService imUserService;
35 |
36 | @RequestMapping("/list")
37 | public Response> list(Page page) { // 分页
38 | LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery();
39 | return Response.ok(imUserService.page(page, queryWrapper));
40 | }
41 |
42 | /**
43 | * 保存
44 | */
45 | @RequestMapping("/save")
46 | public Response save(ImUser imUser) {
47 | boolean result = imUserService.save(imUser);
48 | return result ? Response.ok() : Response.error();
49 | }
50 |
51 | /**
52 | * 修改
53 | */
54 | @RequestMapping("/update")
55 | public Response update(ImUser imUser) {
56 | boolean result = imUserService.updateById(imUser);
57 | return result ? Response.ok() : Response.error();
58 | }
59 |
60 | /**
61 | * 删除
62 | */
63 | @RequestMapping("/remove")
64 | public Response remove(@NotNull(message = "id 不能为空") Integer id) {
65 | boolean result = imUserService.removeById(id);
66 | return result ? Response.ok() : Response.error();
67 | }
68 |
69 | /**
70 | * 批量删除
71 | */
72 | @RequestMapping("/batchRemove")
73 | public Response batchRemove(
74 | @NotNull(message = "不能为空") @Size(min = 1, message = "请至少选择一个") @RequestParam("ids[]") Integer[] ids) {
75 | boolean result = imUserService.removeByIds(CollUtil.toList(ids));
76 | return result ? Response.ok() : Response.error();
77 | }
78 |
79 | }
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/controller/DeptController.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.controller;
2 |
3 | import javax.validation.constraints.NotNull;
4 | import javax.validation.constraints.Size;
5 |
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.validation.annotation.Validated;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.RequestParam;
10 | import org.springframework.web.bind.annotation.RestController;
11 |
12 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
13 | import com.baomidou.mybatisplus.core.metadata.IPage;
14 | import com.baomidou.mybatisplus.core.toolkit.Wrappers;
15 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
16 | import com.chao.cloud.common.entity.Response;
17 | import com.chao.cloud.im.dal.entity.ImDept;
18 | import com.chao.cloud.im.service.ImDeptService;
19 |
20 | import cn.hutool.core.collection.CollUtil;
21 |
22 | /**
23 | * @功能:
24 | * @author: 超君子
25 | * @时间:2019-06-26
26 | * @version 1.0.0
27 | */
28 | @RequestMapping("/im/dept")
29 | @RestController
30 | @Validated
31 | public class DeptController {
32 |
33 | @Autowired
34 | private ImDeptService imDeptService;
35 |
36 | @RequestMapping("/list")
37 | public Response> list(Page page //
38 | ) { // 分页
39 | LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery();
40 | return Response.ok(imDeptService.page(page, queryWrapper));
41 | }
42 |
43 | /**
44 | * 保存
45 | */
46 | @RequestMapping("/save")
47 | public Response save(ImDept imDept) {
48 | boolean result = imDeptService.save(imDept);
49 | return result ? Response.ok() : Response.error();
50 | }
51 |
52 | /**
53 | * 修改
54 | */
55 | @RequestMapping("/update")
56 | public Response update(ImDept imDept) {
57 | boolean result = imDeptService.updateById(imDept);
58 | return result ? Response.ok() : Response.error();
59 | }
60 |
61 | /**
62 | * 删除
63 | */
64 | @RequestMapping("/remove")
65 | public Response remove(@NotNull(message = "id 不能为空") Integer id) {
66 | boolean result = imDeptService.removeById(id);
67 | return result ? Response.ok() : Response.error();
68 | }
69 |
70 | /**
71 | * 批量删除
72 | */
73 | @RequestMapping("/batchRemove")
74 | public Response batchRemove(
75 | @NotNull(message = "不能为空") @Size(min = 1, message = "请至少选择一个") @RequestParam("ids[]") Integer[] ids) {
76 | boolean result = imDeptService.removeByIds(CollUtil.toList(ids));
77 | return result ? Response.ok() : Response.error();
78 | }
79 |
80 | }
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/controller/GroupController.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.controller;
2 |
3 | import javax.validation.constraints.NotNull;
4 | import javax.validation.constraints.Size;
5 |
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.validation.annotation.Validated;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.RequestParam;
10 | import org.springframework.web.bind.annotation.RestController;
11 |
12 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
13 | import com.baomidou.mybatisplus.core.metadata.IPage;
14 | import com.baomidou.mybatisplus.core.toolkit.Wrappers;
15 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
16 | import com.chao.cloud.common.entity.Response;
17 | import com.chao.cloud.im.dal.entity.ImGroup;
18 | import com.chao.cloud.im.service.ImGroupService;
19 |
20 | import cn.hutool.core.collection.CollUtil;
21 |
22 | /**
23 | * @功能:
24 | * @author: 超君子
25 | * @时间:2019-06-26
26 | * @version 1.0.0
27 | */
28 | @RequestMapping("/im/group")
29 | @RestController
30 | @Validated
31 | public class GroupController {
32 |
33 | @Autowired
34 | private ImGroupService imGroupService;
35 |
36 | @RequestMapping("/list")
37 | public Response> list(Page page //
38 | ) { // 分页
39 | LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery();
40 | return Response.ok(imGroupService.page(page, queryWrapper));
41 | }
42 |
43 | /**
44 | * 保存
45 | */
46 | @RequestMapping("/save")
47 | public Response save(ImGroup imGroup) {
48 | boolean result = imGroupService.save(imGroup);
49 | return result ? Response.ok() : Response.error();
50 | }
51 |
52 | /**
53 | * 修改
54 | */
55 | @RequestMapping("/update")
56 | public Response update(ImGroup imGroup) {
57 | boolean result = imGroupService.updateById(imGroup);
58 | return result ? Response.ok() : Response.error();
59 | }
60 |
61 | /**
62 | * 删除
63 | */
64 | @RequestMapping("/remove")
65 | public Response remove(@NotNull(message = "id 不能为空") Integer id) {
66 | boolean result = imGroupService.removeById(id);
67 | return result ? Response.ok() : Response.error();
68 | }
69 |
70 | /**
71 | * 批量删除
72 | */
73 | @RequestMapping("/batchRemove")
74 | public Response batchRemove(
75 | @NotNull(message = "不能为空") @Size(min = 1, message = "请至少选择一个") @RequestParam("ids[]") Integer[] ids) {
76 | boolean result = imGroupService.removeByIds(CollUtil.toList(ids));
77 | return result ? Response.ok() : Response.error();
78 | }
79 |
80 | }
--------------------------------------------------------------------------------
/src/main/resources/templates/layim/chatlog.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
19 |
42 |
43 |
44 |
92 |
93 |
--------------------------------------------------------------------------------
/src/main/resources/static/layui/lay/modules/rate.js:
--------------------------------------------------------------------------------
1 | /** layui-v2.4.5 MIT License By https://www.layui.com */
2 | ;layui.define("jquery",function(e){"use strict";var a=layui.jquery,i={config:{},index:layui.rate?layui.rate.index+1e4:0,set:function(e){var i=this;return i.config=a.extend({},i.config,e),i},on:function(e,a){return layui.onevent.call(this,n,e,a)}},l=function(){var e=this,a=e.config;return{setvalue:function(a){e.setvalue.call(e,a)},config:a}},n="rate",t="layui-rate",o="layui-icon-rate",s="layui-icon-rate-solid",u="layui-icon-rate-half",r="layui-icon-rate-solid layui-icon-rate-half",c="layui-icon-rate-solid layui-icon-rate",f="layui-icon-rate layui-icon-rate-half",v=function(e){var l=this;l.index=++i.index,l.config=a.extend({},l.config,i.config,e),l.render()};v.prototype.config={length:5,text:!1,readonly:!1,half:!1,value:0,theme:""},v.prototype.render=function(){var e=this,i=e.config,l=i.theme?'style="color: '+i.theme+';"':"";i.elem=a(i.elem),parseInt(i.value)!==i.value&&(i.half||(i.value=Math.ceil(i.value)-i.value<.5?Math.ceil(i.value):Math.floor(i.value)));for(var n='",u=1;u<=i.length;u++){var r=' ";i.half&&parseInt(i.value)!==i.value&&u==Math.ceil(i.value)?n=n+' ":n+=r}n+=" "+(i.text?''+i.value+"星":"")+" ";var c=i.elem,f=c.next("."+t);f[0]&&f.remove(),e.elemTemp=a(n),i.span=e.elemTemp.next("span"),i.setText&&i.setText(i.value),c.html(e.elemTemp),c.addClass("layui-inline"),i.readonly||e.action()},v.prototype.setvalue=function(e){var a=this,i=a.config;i.value=e,a.render()},v.prototype.action=function(){var e=this,i=e.config,l=e.elemTemp,n=l.find("i").width();l.children("li").each(function(e){var t=e+1,v=a(this);v.on("click",function(e){if(i.value=t,i.half){var o=e.pageX-a(this).offset().left;o<=n/2&&(i.value=i.value-.5)}i.text&&l.next("span").text(i.value+"星"),i.choose&&i.choose(i.value),i.setText&&i.setText(i.value)}),v.on("mousemove",function(e){if(l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+t+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half){var c=e.pageX-a(this).offset().left;c<=n/2&&v.children("i").addClass(u).removeClass(s)}}),v.on("mouseleave",function(){l.find("i").each(function(){a(this).addClass(o).removeClass(r)}),l.find("i:lt("+Math.floor(i.value)+")").each(function(){a(this).addClass(s).removeClass(f)}),i.half&&parseInt(i.value)!==i.value&&l.children("li:eq("+Math.floor(i.value)+")").children("i").addClass(u).removeClass(c)})})},v.prototype.events=function(){var e=this;e.config},i.render=function(e){var a=new v(e);return l.call(a)},e(n,i)});
--------------------------------------------------------------------------------
/src/main/resources/static/layui/css/modules/layim/html/chatlog.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 聊天记录
9 |
10 |
11 |
14 |
15 |
16 |
17 |
20 |
21 |
22 |
23 |
24 |
33 |
34 |
38 |
39 |
40 |
41 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/src/main/resources/static/layui/lay/modules/tree.js:
--------------------------------------------------------------------------------
1 | /** layui-v2.4.5 MIT License By https://www.layui.com */
2 | ;layui.define("jquery",function(e){"use strict";var o=layui.$,a=layui.hint(),i="layui-tree-enter",r=function(e){this.options=e},t={arrow:["",""],checkbox:["",""],radio:["",""],branch:["",""],leaf:""};r.prototype.init=function(e){var o=this;e.addClass("layui-box layui-tree"),o.options.skin&&e.addClass("layui-tree-skin-"+o.options.skin),o.tree(e),o.on(e)},r.prototype.tree=function(e,a){var i=this,r=i.options,n=a||r.nodes;layui.each(n,function(a,n){var l=n.children&&n.children.length>0,c=o(''),s=o(["",function(){return l?''+(n.spread?t.arrow[1]:t.arrow[0])+" ":""}(),function(){return r.check?''+("checkbox"===r.check?t.checkbox[0]:"radio"===r.check?t.radio[0]:"")+" ":""}(),function(){return'"+(''+(l?n.spread?t.branch[1]:t.branch[0]:t.leaf)+" ")+(""+(n.name||"未命名")+" ")}()," "].join(""));l&&(s.append(c),i.tree(c,n.children)),e.append(s),"function"==typeof r.click&&i.click(s,n),i.spread(s,n),r.drag&&i.drag(s,n)})},r.prototype.click=function(e,o){var a=this,i=a.options;e.children("a").on("click",function(e){layui.stope(e),i.click(o)})},r.prototype.spread=function(e,o){var a=this,i=(a.options,e.children(".layui-tree-spread")),r=e.children("ul"),n=e.children("a"),l=function(){e.data("spread")?(e.data("spread",null),r.removeClass("layui-show"),i.html(t.arrow[0]),n.find(".layui-icon").html(t.branch[0])):(e.data("spread",!0),r.addClass("layui-show"),i.html(t.arrow[1]),n.find(".layui-icon").html(t.branch[1]))};r[0]&&(i.on("click",l),n.on("dblclick",l))},r.prototype.on=function(e){var a=this,r=a.options,t="layui-tree-drag";e.find("i").on("selectstart",function(e){return!1}),r.drag&&o(document).on("mousemove",function(e){var i=a.move;if(i.from){var r=(i.to,o('
'));e.preventDefault(),o("."+t)[0]||o("body").append(r);var n=o("."+t)[0]?o("."+t):r;n.addClass("layui-show").html(i.from.elem.children("a").html()),n.css({left:e.pageX+10,top:e.pageY+10})}}).on("mouseup",function(){var e=a.move;e.from&&(e.from.elem.children("a").removeClass(i),e.to&&e.to.elem.children("a").removeClass(i),a.move={},o("."+t).remove())})},r.prototype.move={},r.prototype.drag=function(e,a){var r=this,t=(r.options,e.children("a")),n=function(){var t=o(this),n=r.move;n.from&&(n.to={item:a,elem:e},t.addClass(i))};t.on("mousedown",function(){var o=r.move;o.from={item:a,elem:e}}),t.on("mouseenter",n).on("mousemove",n).on("mouseleave",function(){var e=o(this),a=r.move;a.from&&(delete a.to,e.removeClass(i))})},e("tree",function(e){var i=new r(e=e||{}),t=o(e.elem);return t[0]?void i.init(t):a.error("layui.tree 没有找到"+e.elem+"元素")})});
--------------------------------------------------------------------------------
/src/main/resources/static/layui/lay/modules/util.js:
--------------------------------------------------------------------------------
1 | /** layui-v2.4.5 MIT License By https://www.layui.com */
2 | ;layui.define("jquery",function(t){"use strict";var e=layui.$,i={fixbar:function(t){var i,a,n="layui-fixbar",r="layui-fixbar-top",o=e(document),l=e("body");t=e.extend({showHeight:200},t),t.bar1=t.bar1===!0?"":t.bar1,t.bar2=t.bar2===!0?"":t.bar2,t.bgcolor=t.bgcolor?"background-color:"+t.bgcolor:"";var c=[t.bar1,t.bar2,""],g=e(['',t.bar1?''+c[0]+" ":"",t.bar2?''+c[1]+" ":"",''+c[2]+" "," "].join("")),s=g.find("."+r),u=function(){var e=o.scrollTop();e>=t.showHeight?i||(s.show(),i=1):i&&(s.hide(),i=0)};e("."+n)[0]||("object"==typeof t.css&&g.css(t.css),l.append(g),u(),g.find("li").on("click",function(){var i=e(this),a=i.attr("lay-type");"top"===a&&e("html,body").animate({scrollTop:0},200),t.click&&t.click.call(this,a)}),o.on("scroll",function(){clearTimeout(a),a=setTimeout(function(){u()},100)}))},countdown:function(t,e,i){var a=this,n="function"==typeof e,r=new Date(t).getTime(),o=new Date(!e||n?(new Date).getTime():e).getTime(),l=r-o,c=[Math.floor(l/864e5),Math.floor(l/36e5)%24,Math.floor(l/6e4)%60,Math.floor(l/1e3)%60];n&&(i=e);var g=setTimeout(function(){a.countdown(t,o+1e3,i)},1e3);return i&&i(l>0?c:[0,0,0,0],e,g),l<=0&&clearTimeout(g),g},timeAgo:function(t,e){var i=this,a=[[],[]],n=(new Date).getTime()-new Date(t).getTime();return n>6912e5?(n=new Date(t),a[0][0]=i.digit(n.getFullYear(),4),a[0][1]=i.digit(n.getMonth()+1),a[0][2]=i.digit(n.getDate()),e||(a[1][0]=i.digit(n.getHours()),a[1][1]=i.digit(n.getMinutes()),a[1][2]=i.digit(n.getSeconds())),a[0].join("-")+" "+a[1].join(":")):n>=864e5?(n/1e3/60/60/24|0)+"天前":n>=36e5?(n/1e3/60/60|0)+"小时前":n>=12e4?(n/1e3/60|0)+"分钟前":n<0?"未来":"刚刚"},digit:function(t,e){var i="";t=String(t),e=e||2;for(var a=t.length;a /g,">").replace(/'/g,"'").replace(/"/g,""")}};!function(t,e,i){"$:nomunge";function a(){n=e[l](function(){r.each(function(){var e=t(this),i=e.width(),a=e.height(),n=t.data(this,g);(i!==n.w||a!==n.h)&&e.trigger(c,[n.w=i,n.h=a])}),a()},o[s])}var n,r=t([]),o=t.resize=t.extend(t.resize,{}),l="setTimeout",c="resize",g=c+"-special-event",s="delay",u="throttleWindow";o[s]=250,o[u]=!0,t.event.special[c]={setup:function(){if(!o[u]&&this[l])return!1;var e=t(this);r=r.add(e),t.data(this,g,{w:e.width(),h:e.height()}),1===r.length&&a()},teardown:function(){if(!o[u]&&this[l])return!1;var e=t(this);r=r.not(e),e.removeData(g),r.length||clearTimeout(n)},add:function(e){function a(e,a,r){var o=t(this),l=t.data(this,g)||{};l.w=a!==i?a:o.width(),l.h=r!==i?r:o.height(),n.apply(this,arguments)}if(!o[u]&&this[l])return!1;var n;return t.isFunction(e)?(n=e,a):(n=e.handler,void(e.handler=a))}}}(e,window),t("util",i)});
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/websocket/BaseWsSocket.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.websocket;
2 |
3 | import cn.hutool.core.util.StrUtil;
4 | import cn.hutool.json.JSONUtil;
5 | import cn.hutool.log.StaticLog;
6 | import com.chao.cloud.common.exception.BusinessException;
7 | import com.chao.cloud.im.websocket.constant.MsgEnum;
8 | import com.chao.cloud.im.websocket.model.WsMsgDTO;
9 |
10 | import javax.websocket.Session;
11 | import java.io.IOException;
12 | import java.util.Map;
13 | import java.util.concurrent.ConcurrentHashMap;
14 |
15 | public abstract class BaseWsSocket {
16 |
17 | private static Map, Map> pool = new ConcurrentHashMap<>();
18 | // concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
19 | protected Map> webSocketSet = getWsSocketMap(this.getClass());
20 | // 与某个客户端的连接会话,需要通过它来给客户端发送数据
21 | protected Session session;
22 | // 接收sid
23 | protected T sid;
24 |
25 | /**
26 | * 连接建立成功调用的方法
27 | */
28 | public final void open(Session session, T sid) {
29 | this.session = session;
30 | webSocketSet.put(sid, this); // 加入set中
31 | this.sid = sid;
32 | try {
33 | StaticLog.info("[{}:有新用户开始连接:user={},当前在线人数为:{}]", serverName(), sid, webSocketSet.size());
34 | sendMessage(WsMsgDTO.buildMsg(MsgEnum.START, "连接成功!"));
35 | } catch (Exception e) {
36 | StaticLog.error("[{}:user={},websocket IO异常:{}]", serverName(), sid, e.getMessage());
37 | }
38 | }
39 |
40 | /**
41 | * 连接关闭调用的方法
42 | */
43 | protected void onClose() {
44 | webSocketSet.remove(this.sid); // 从set中删除
45 | StaticLog.info("{}:有一连接关闭!当前在线人数为:{}", serverName(), webSocketSet.size());
46 | }
47 |
48 | /**
49 | * 收到客户端消息后调用的方法
50 | *
51 | * @param msg 客户端发送过来的消息
52 | * @throws IOException
53 | */
54 | protected void onMessage(String msg, Session session) {
55 | StaticLog.info("{}:收到来自用户[{}]的信息:{}", serverName(), sid, msg);
56 | }
57 |
58 | protected void onError(Session session, Throwable error) {
59 | StaticLog.error("{}:发生错误--->{}", serverName(), error);
60 | }
61 |
62 | /**
63 | * 实现服务器主动推送
64 | */
65 | public void sendMessage(Object msg) {
66 | try {
67 | this.session.getBasicRemote().sendText(JSONUtil.toJsonStr(msg));
68 | } catch (Exception e) {
69 | throw new BusinessException(e.getMessage());
70 | }
71 | }
72 |
73 | protected String serverName() {
74 | return StrUtil.lowerFirst(this.getClass().getSimpleName());
75 | }
76 |
77 | ;
78 |
79 | /**
80 | * 是否存在
81 | *
82 | * @param sid
83 | * @return
84 | */
85 | protected boolean exist(T sid) {
86 | return webSocketSet.containsKey(sid);
87 | }
88 |
89 | public T getSid() {
90 | return sid;
91 | }
92 |
93 | private static Map getWsSocketMap(Class extends BaseWsSocket> clazz) {
94 | Map map = pool.get(clazz);
95 | if (map == null) {
96 | synchronized (BaseWsSocket.class) {
97 | map = pool.get(clazz);
98 | if (null == map) {
99 | map = new ConcurrentHashMap<>();
100 | pool.put(clazz, map);
101 | }
102 | }
103 | }
104 | return map;
105 | }
106 |
107 | }
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/controller/FileController.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.controller;
2 |
3 | import java.io.BufferedOutputStream;
4 | import java.io.OutputStream;
5 |
6 | import javax.servlet.http.HttpServletRequest;
7 | import javax.servlet.http.HttpServletResponse;
8 | import javax.validation.constraints.NotEmpty;
9 | import javax.validation.constraints.NotNull;
10 |
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.validation.annotation.Validated;
13 | import org.springframework.web.bind.annotation.PostMapping;
14 | import org.springframework.web.bind.annotation.RequestMapping;
15 | import org.springframework.web.bind.annotation.RestController;
16 | import org.springframework.web.multipart.MultipartFile;
17 |
18 | import com.baomidou.mybatisplus.extension.api.R;
19 | import com.chao.cloud.common.extra.ftp.IFileOperation;
20 | import com.chao.cloud.common.extra.ftp.annotation.FtpConfig;
21 | import com.chao.cloud.im.domain.dto.LayFileDTO;
22 |
23 | import cn.hutool.extra.servlet.ServletUtil;
24 | import lombok.extern.slf4j.Slf4j;
25 |
26 | /**
27 | * 文件上传
28 | * @功能:
29 | * @author: 薛超
30 | * @时间: 2019年5月30日
31 | * @version 1.0.0
32 | */
33 | @Slf4j
34 | @Validated
35 | @RestController
36 | @RequestMapping("file")
37 | public class FileController {
38 |
39 | private static final String MESSAGE = "文件不能为空";
40 |
41 | @Autowired
42 | private IFileOperation fileOperation;
43 |
44 | @Autowired
45 | private FtpConfig ftpConfig;
46 |
47 | /**
48 | * 图片上传
49 | * @return
50 | * @throws Exception
51 | */
52 | @PostMapping(value = "uploadImg")
53 | public R uploadImg(@NotNull(message = MESSAGE) MultipartFile file, HttpServletRequest request)
54 | throws Exception {
55 | String uploadImg = fileOperation.uploadImg(file.getInputStream(), file.getOriginalFilename());
56 | // 获取域名
57 | String realm = ServletUtil.getHeaderIgnoreCase(request, "Origin");
58 | return R.ok(LayFileDTO.builder().src(realm + uploadImg).domain(ftpConfig.getRealm()).build());
59 | }
60 |
61 | /**
62 | * 上传文件
63 | * @param file
64 | * @return
65 | * @throws Exception
66 | */
67 | @PostMapping(value = "upload")
68 | public R upload(@NotNull(message = MESSAGE) MultipartFile file, HttpServletRequest request)
69 | throws Exception {
70 | String name = file.getOriginalFilename();
71 | String uploadImg = fileOperation.uploadInputStream(file.getInputStream(), name);
72 | // 获取域名
73 | String realm = ServletUtil.getHeaderIgnoreCase(request, "Origin");
74 | return R.ok(LayFileDTO.builder().src(realm + uploadImg).domain(ftpConfig.getRealm()).name(name).build());
75 | }
76 |
77 | /**
78 | * 文件下载
79 | * @return
80 | * @throws Exception
81 | */
82 | @RequestMapping(value = "downLoad")
83 | public void downLoad(@NotEmpty(message = MESSAGE) String fileName, HttpServletResponse response) throws Exception {
84 | String finalName = fileName.substring(fileName.lastIndexOf("/"));
85 | try (OutputStream os = new BufferedOutputStream(response.getOutputStream())) {
86 | byte[] downLoad = null;
87 | response.reset();
88 | response.setContentType("application/x-msdownload"); // 设置返回的文件类型
89 | response.setHeader("Content-Disposition", "attachment;filename=" + finalName);
90 | os.write(downLoad);
91 | os.flush();
92 | } catch (Exception e) {
93 | log.info("[下载异常信息:fileName={},error={}]", fileName, e.getMessage());
94 | log.error("[下载异常{}]", e);
95 | }
96 |
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/ai/service/AipUnitService.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.ai.service;
2 |
3 | import java.io.InputStream;
4 | import java.util.HashMap;
5 | import java.util.List;
6 | import java.util.stream.Collectors;
7 |
8 | import org.json.JSONObject;
9 | import org.springframework.beans.factory.InitializingBean;
10 |
11 | import com.baidu.aip.client.BaseClient;
12 | import com.baidu.aip.http.AipRequest;
13 | import com.baidu.aip.http.EBodyFormat;
14 | import com.chao.cloud.im.ai.constant.AiConstant;
15 | import com.chao.cloud.im.ai.model.BAiResp;
16 | import com.chao.cloud.im.ai.model.BAiResp.Result.Response;
17 | import com.chao.cloud.im.ai.model.BAiResp.Result.Response.Action;
18 |
19 | import cn.hutool.core.bean.BeanUtil;
20 | import cn.hutool.core.collection.CollUtil;
21 | import cn.hutool.core.io.IoUtil;
22 | import cn.hutool.core.io.resource.ResourceUtil;
23 | import cn.hutool.core.lang.Console;
24 | import cn.hutool.core.util.CharsetUtil;
25 | import cn.hutool.core.util.RandomUtil;
26 | import cn.hutool.core.util.ReUtil;
27 | import cn.hutool.core.util.StrUtil;
28 | import cn.hutool.json.JSONUtil;
29 |
30 | /**
31 | * http://ai.baidu.com/docs#/UNIT-v2-service-API/top
32 | * @功能:百度:UNIT机器人对话API文档
33 | * @author: 薛超
34 | * @时间: 2019年7月5日
35 | * @version 1.0.0
36 | */
37 | public class AipUnitService extends BaseClient implements InitializingBean {
38 |
39 | public AipUnitService(String appId, String apiKey, String secretKey) {
40 | super(appId, apiKey, secretKey);
41 | }
42 |
43 | private String parmTemplate;// request 模板
44 |
45 | /**
46 | * 根据文本返回
47 | * @param text 问题
48 | * @param userId 内部用户id
49 | * @return
50 | */
51 | public String text(String text, Integer userId) {
52 | AipRequest request = new AipRequest();
53 | preOperation(request);
54 | // 请求参数
55 | String params = StrUtil.format(parmTemplate, text, userId);
56 | HashMap map = JSONUtil.toBean(params, HashMap.class);
57 | request.setBody(map);
58 | // 设置请求路径
59 | request.setUri(AiConstant.BAI_URL);
60 | request.setBodyFormat(EBodyFormat.RAW_JSON);
61 | postOperation(request);
62 | JSONObject server = requestServer(request);
63 | BAiResp resp = JSONUtil.toBean(server.toString(), BAiResp.class);
64 | if (!AiConstant.TAI_SUCCESS.equals(resp.getError_code())) {
65 | return resp.getError_msg();
66 | }
67 | List list = resp.getResult().getResponse_list();
68 | if (CollUtil.isNotEmpty(list)) {
69 | // 获取需要解析的数据
70 | list = list.stream()
71 | .filter(l -> (!AiConstant.GREET_CODE.equals(l.getOrigin())
72 | || AiConstant.CONFIDENCE_LIMIT < l.getSchema().getIntent_confidence()) // 问候类型 大于50
73 | && CollUtil.isNotEmpty(l.getAction_list())// action不能为空
74 | && l.getAction_list().stream()//
75 | .filter(a -> ReUtil.isMatch(AiConstant.BAI_OK_TYPE, a.getType())).count() > 0)// 只解析这几种类型
76 | .collect(Collectors.toList());
77 | // 继续解析
78 | Console.log(JSONUtil.toJsonStr(list));
79 | Response first = CollUtil.getFirst(list);
80 | // 判断信任值
81 | if (!BeanUtil.isEmpty(first) && CollUtil.isNotEmpty(first.getAction_list())) {
82 | List action_list = first.getAction_list();
83 | // 随机取一条
84 | int index = RandomUtil.randomInt(action_list.size());
85 | return action_list.get(index).getSay();
86 | }
87 | }
88 | return AiConstant.ERROR_RESULT;
89 | }
90 |
91 | @Override
92 | public void afterPropertiesSet() throws Exception {
93 | // 解析json
94 | try (InputStream in = ResourceUtil.getStream(AiConstant.BAI_REQUEST_TEMPLATE);) {
95 | parmTemplate = IoUtil.read(in, CharsetUtil.CHARSET_UTF_8);
96 | }
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/src/main/resources/static/layui/lay/modules/carousel.js:
--------------------------------------------------------------------------------
1 | /** layui-v2.4.5 MIT License By https://www.layui.com */
2 | ;layui.define("jquery",function(e){"use strict";var i=layui.$,n=(layui.hint(),layui.device(),{config:{},set:function(e){var n=this;return n.config=i.extend({},n.config,e),n},on:function(e,i){return layui.onevent.call(this,t,e,i)}}),t="carousel",a="layui-this",l=">*[carousel-item]>*",o="layui-carousel-left",r="layui-carousel-right",d="layui-carousel-prev",s="layui-carousel-next",u="layui-carousel-arrow",c="layui-carousel-ind",m=function(e){var t=this;t.config=i.extend({},t.config,n.config,e),t.render()};m.prototype.config={width:"600px",height:"280px",full:!1,arrow:"hover",indicator:"inside",autoplay:!0,interval:3e3,anim:"",trigger:"click",index:0},m.prototype.render=function(){var e=this,n=e.config;n.elem=i(n.elem),n.elem[0]&&(e.elemItem=n.elem.find(l),n.index<0&&(n.index=0),n.index>=e.elemItem.length&&(n.index=e.elemItem.length-1),n.interval<800&&(n.interval=800),n.full?n.elem.css({position:"fixed",width:"100%",height:"100%",zIndex:9999}):n.elem.css({width:n.width,height:n.height}),n.elem.attr("lay-anim",n.anim),e.elemItem.eq(n.index).addClass(a),e.elemItem.length<=1||(e.indicator(),e.arrow(),e.autoplay(),e.events()))},m.prototype.reload=function(e){var n=this;clearInterval(n.timer),n.config=i.extend({},n.config,e),n.render()},m.prototype.prevIndex=function(){var e=this,i=e.config,n=i.index-1;return n<0&&(n=e.elemItem.length-1),n},m.prototype.nextIndex=function(){var e=this,i=e.config,n=i.index+1;return n>=e.elemItem.length&&(n=0),n},m.prototype.addIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index+e,n.index>=i.elemItem.length&&(n.index=0)},m.prototype.subIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index-e,n.index<0&&(n.index=i.elemItem.length-1)},m.prototype.autoplay=function(){var e=this,i=e.config;i.autoplay&&(e.timer=setInterval(function(){e.slide()},i.interval))},m.prototype.arrow=function(){var e=this,n=e.config,t=i([''+("updown"===n.anim?"":"")+" ",''+("updown"===n.anim?"":"")+" "].join(""));n.elem.attr("lay-arrow",n.arrow),n.elem.find("."+u)[0]&&n.elem.find("."+u).remove(),n.elem.append(t),t.on("click",function(){var n=i(this),t=n.attr("lay-type");e.slide(t)})},m.prototype.indicator=function(){var e=this,n=e.config,t=e.elemInd=i(['',function(){var i=[];return layui.each(e.elemItem,function(e){i.push(" ")}),i.join("")}()," "].join(""));n.elem.attr("lay-indicator",n.indicator),n.elem.find("."+c)[0]&&n.elem.find("."+c).remove(),n.elem.append(t),"updown"===n.anim&&t.css("margin-top",-(t.height()/2)),t.find("li").on("hover"===n.trigger?"mouseover":n.trigger,function(){var t=i(this),a=t.index();a>n.index?e.slide("add",a-n.index):a ajaxLogin(@Valid LoginVO vo, HttpSession session) {
55 | String random = (String) session.getAttribute(randomCookie);
56 | if (StrUtil.isBlank(random)) {
57 | throw new BusinessException("请刷新验证码");
58 | }
59 | if (!vo.getVerify().equals(random)) {
60 | throw new BusinessException("请输入正确的验证码");
61 | }
62 | // 根据登录名获取用户
63 | ImUser user = imUserService.getOne(Wrappers.lambdaQuery().eq(ImUser::getUserName, vo.getUserName()));
64 | if (BeanUtil.isEmpty(user)) {
65 | throw new BusinessException("用户不存在");
66 | }
67 | if (!vo.getPassword().equals(user.getPassword())) {
68 | throw new BusinessException("密码错误");
69 | }
70 | if (!ImConstant.Status.OK.equals(user.getStatus())) {
71 | throw new BusinessException("账号被冻结");
72 | }
73 | LoginDTO dto = BeanUtil.toBean(user, LoginDTO.class);
74 | session.setAttribute(token_key, dto);
75 | return Response.ok();
76 |
77 | }
78 |
79 | @RequestMapping("/logout")
80 | public String logout(HttpSession session) {
81 | session.invalidate();
82 | return "redirect:/login";
83 | }
84 |
85 | /**
86 | * 生成验证码
87 | */
88 | @RequestMapping("/getVerify")
89 | public void getVerify(HttpServletRequest request, HttpServletResponse response) {
90 | try (OutputStream os = response.getOutputStream()) {
91 | response.setContentType("image/jpeg");// 设置相应类型,告诉浏览器输出的内容为图片
92 | response.setHeader("Pragma", "No-cache");// 设置响应头信息,告诉浏览器不要缓存此内容
93 | response.setHeader("Cache-Control", "no-cache");
94 | response.setDateHeader("Expire", 0);
95 | // 制作验证码
96 | HyalineCircleCaptcha captcha = HyalineCaptchaUtil.createCircleCaptcha(100, 42, 4, 3);
97 | String code = captcha.getCode();
98 | log.info("[验证码: {}]", code);
99 | // 存入session
100 | HttpSession session = request.getSession();
101 | // 将生成的随机字符串保存到session中
102 | session.removeAttribute(randomCookie);
103 | session.setAttribute(randomCookie, code);
104 | // 输出
105 | captcha.write(os);
106 | } catch (Exception e) {
107 | log.error("获取验证码失败--->{} ", e);
108 | }
109 | }
110 |
111 | }
--------------------------------------------------------------------------------
/src/main/resources/static/layui/css/modules/layim/mobile/layim.css:
--------------------------------------------------------------------------------
1 | /** layui-v1.0.7 MIT License By http://www.layui.com */
2 | .layim-chat-main,.layim-chat-main ul{overflow-x:hidden}html #layuicss-skinlayim-mobilecss{display:none;position:absolute;width:1989px}.layim-chat,.layim-chat-main,.layim-chat-title{position:fixed;left:0;right:0}.layim-chat{bottom:0;top:0;background-color:#eee}.layim-chat-title{top:0;height:50px;line-height:50px;text-align:center;background-color:#36373C;color:#fff;font-size:16px}.layim-chat-status{padding-left:5px;font-size:14px}.layim-chat-main{width:100%;bottom:85px;top:50px;padding:15px;overflow-y:auto;-webkit-box-sizing:border-box!important;-moz-box-sizing:border-box!important;box-sizing:border-box!important}.layim-chat-main[nonetitle]{top:0}.layim-chat-main ul li{position:relative;font-size:0;margin-bottom:10px;padding-left:60px;min-height:68px}.layim-chat-text,.layim-chat-user{display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:14px}.layim-chat-user{position:absolute;left:3px}.layim-chat-user img{width:40px;height:40px;border-radius:100%}.layim-chat-user cite{position:absolute;left:60px;top:-2px;width:500px;line-height:24px;font-size:12px;white-space:nowrap;color:#999;text-align:left;font-style:normal}.layim-chat-user cite i{padding-left:15px;font-style:normal}.layim-chat-text{position:relative;min-height:22px;line-height:22px;margin-top:25px;padding:8px 15px;background-color:#fff;border-radius:3px;color:#333;word-break:break-all}.layim-chat-text:after{content:'';position:absolute;left:-10px;top:13px;width:0;height:0;border-style:solid dashed dashed;border-color:#fff transparent transparent;overflow:hidden;border-width:10px}.layim-chat-text a{color:#33DF83}.layim-chat-text img{max-width:100%;vertical-align:middle}.layim-chat-text .layui-layim-file,.layui-layim-file{display:block;text-align:center}.layim-chat-text .layui-layim-file{color:#333}.layui-layim-file:hover{opacity:.9}.layui-layim-file i{font-size:80px;line-height:80px}.layui-layim-file cite{display:block;line-height:20px;font-size:14px}.layim-chat-main ul .layim-chat-mine{text-align:right;padding-left:0;padding-right:60px}.layim-chat-mine .layim-chat-user{left:auto;right:3px}.layim-chat-mine .layim-chat-user cite{left:auto;right:60px;text-align:right}.layim-chat-mine .layim-chat-user cite i{padding-left:0;padding-right:15px}.layim-chat-mine .layim-chat-text{margin-left:0;text-align:left;background-color:#5FB878;color:#fff}.layim-chat-mine .layim-chat-text:after{left:auto;right:-10px;border-top-color:#5FB878}.layim-chat-mine .layim-chat-text a{color:#fff}.layim-chat-main ul .layim-chat-system{min-height:0;margin:20px 0 5px;padding:0}.layim-chat-system{margin:10px 0;text-align:center}.layim-chat-system span{display:inline-block;line-height:30px;padding:0 15px;border-radius:3px;background-color:#ddd;color:#fff;font-size:14px}.layim-chat-footer{position:fixed;bottom:0;left:10px;right:10px;height:80px}.layim-chat-send{display:-webkit-box;display:-webkit-flex;display:flex}.layim-chat-send input{-webkit-box-flex:1;-webkit-flex:1;flex:1;height:36px;padding-left:5px;border:0;background-color:#fff;border-radius:3px}.layim-chat-send button{border-radius:3px;height:36px;padding:0 10px;border:0;margin-left:10px;background-color:#5FB878;color:#fff}.layim-chat-tool{position:relative;width:100%;overflow-x:auto;padding:0;height:38px;line-height:38px;margin-top:5px;font-size:0;white-space:nowrap}.layim-chat-tool span{position:relative;margin:0 10px;display:inline-block;*display:inline;*zoom:1;vertical-align:top;font-size:24px;cursor:pointer}.layim-chat-tool .layim-tool-log{position:absolute;right:5px;font-size:14px}.layim-tool-log i{position:relative;top:2px;margin-right:5px;font-size:20px;color:#999}.layim-tool-image input{position:absolute;font-size:0;left:0;top:0;width:100%;height:100%;opacity:.01;filter:Alpha(opacity=1);cursor:pointer}.layim-layer{position:fixed;bottom:85px;left:10px;right:10px;margin:0 auto}.layui-layim-face{position:relative;max-height:180px;overflow:auto;padding:10px;font-size:0}.layui-layim-face li{cursor:pointer;display:inline-block;vertical-align:bottom;padding:5px 2px;text-align:center;width:10%;-webkit-box-sizing:border-box!important;-moz-box-sizing:border-box!important;box-sizing:border-box!important}.layui-layim-face li img{width:22px;height:22px}
--------------------------------------------------------------------------------
/src/main/resources/static/layui/lay/modules/laypage.js:
--------------------------------------------------------------------------------
1 | /** layui-v2.4.5 MIT License By https://www.layui.com */
2 | ;layui.define(function(e){"use strict";var a=document,t="getElementById",n="getElementsByTagName",i="laypage",r="layui-disabled",u=function(e){var a=this;a.config=e||{},a.config.index=++s.index,a.render(!0)};u.prototype.type=function(){var e=this.config;if("object"==typeof e.elem)return void 0===e.elem.length?2:3},u.prototype.view=function(){var e=this,a=e.config,t=a.groups="groups"in a?0|a.groups:5;a.layout="object"==typeof a.layout?a.layout:["prev","page","next"],a.count=0|a.count,a.curr=0|a.curr||1,a.limits="object"==typeof a.limits?a.limits:[10,20,30,40,50],a.limit=0|a.limit||10,a.pages=Math.ceil(a.count/a.limit)||1,a.curr>a.pages&&(a.curr=a.pages),t<0?t=1:t>a.pages&&(t=a.pages),a.prev="prev"in a?a.prev:"上一页",a.next="next"in a?a.next:"下一页";var n=a.pages>t?Math.ceil((a.curr+(t>1?1:0))/(t>0?t:1)):1,i={prev:function(){return a.prev?''+a.prev+" ":""}(),page:function(){var e=[];if(a.count<1)return"";n>1&&a.first!==!1&&0!==t&&e.push(''+(a.first||1)+" ");var i=Math.floor((t-1)/2),r=n>1?a.curr-i:1,u=n>1?function(){var e=a.curr+(t-i-1);return e>a.pages?a.pages:e}():t;for(u-r2&&e.push('… ');r<=u;r++)r===a.curr?e.push('"+r+" "):e.push(''+r+" ");return a.pages>t&&a.pages>u&&a.last!==!1&&(u+1…'),0!==t&&e.push(''+(a.last||a.pages)+" ")),e.join("")}(),next:function(){return a.next?''+a.next+" ":""}(),count:'共 '+a.count+" 条 ",limit:function(){var e=[''];return layui.each(a.limits,function(t,n){e.push('"+n+" 条/页 ")}),e.join("")+" "}(),refresh:['',' '," "].join(""),skip:function(){return['到第',' ','页确定 '," "].join("")}()};return['',function(){var e=[];return layui.each(a.layout,function(a,t){i[t]&&e.push(i[t])}),e.join("")}(),"
"].join("")},u.prototype.jump=function(e,a){if(e){var t=this,i=t.config,r=e.children,u=e[n]("button")[0],l=e[n]("input")[0],p=e[n]("select")[0],c=function(){var e=0|l.value.replace(/\s|\D/g,"");e&&(i.curr=e,t.render())};if(a)return c();for(var o=0,y=r.length;oi.pages||(i.curr=e,t.render())});p&&s.on(p,"change",function(){var e=this.value;i.curr*e>i.count&&(i.curr=Math.ceil(i.count/e)),i.limit=e,t.render()}),u&&s.on(u,"click",function(){c()})}},u.prototype.skip=function(e){if(e){var a=this,t=e[n]("input")[0];t&&s.on(t,"keyup",function(t){var n=this.value,i=t.keyCode;/^(37|38|39|40)$/.test(i)||(/\D/.test(n)&&(this.value=n.replace(/\D/,"")),13===i&&a.jump(e,!0))})}},u.prototype.render=function(e){var n=this,i=n.config,r=n.type(),u=n.view();2===r?i.elem&&(i.elem.innerHTML=u):3===r?i.elem.html(u):a[t](i.elem)&&(a[t](i.elem).innerHTML=u),i.jump&&i.jump(i,e);var s=a[t]("layui-laypage-"+i.index);n.jump(s),i.hash&&!e&&(location.hash="!"+i.hash+"="+i.curr),n.skip(s)};var s={render:function(e){var a=new u(e);return a.index},index:layui.laypage?layui.laypage.index+1e4:0,on:function(e,a,t){return e.attachEvent?e.attachEvent("on"+a,function(a){a.target=a.srcElement,t.call(e,a)}):e.addEventListener(a,t,!1),this}};e(i,s)});
--------------------------------------------------------------------------------
/src/main/resources/static/login/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Login
4 |
5 |
6 |
7 |
8 |
9 |
10 |
15 |
16 |
Login
17 |
32 |
33 |
The wood of embracing is born at the end of the millimeter; the platform of nine layers
34 | starts from the earth; the journey of a thousand miles starts from the foot.
35 |
合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。
36 |
37 |
38 |
39 |
143 |
144 |
--------------------------------------------------------------------------------
/src/main/resources/static/login/login.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: "Microsoft YaHei", 微软雅黑, "Microsoft JhengHei", 华文细黑,
3 | STHeiti, MingLiu;
4 | text-align: center;
5 | color: #fff;
6 | margin: 0;
7 | height: 100%;
8 | overflow: hidden;
9 | }
10 |
11 | .bgdiv {
12 | width: 100%;
13 | height: 100%;
14 | position: absolute;
15 | z-index: -999;
16 | }
17 |
18 | .bgdiv img {
19 | width: 100%;
20 | height: 100%;
21 | border: 0px;
22 | }
23 |
24 | .content {
25 | padding-top: 8%;
26 | margin: 0 auto;
27 | }
28 |
29 | h1 {
30 | font-size: 30px;
31 | font-weight: 700;
32 | text-shadow: 0 1px 4px rgba(0, 0, 0, .2);
33 | }
34 |
35 | form {
36 | position: relative;
37 | width: 305px;
38 | margin: 15px auto 0 auto;
39 | text-align: center;
40 | }
41 |
42 | input {
43 | width: 270px;
44 | height: 42px;
45 | line-height: 42px;
46 | margin-top: 25px;
47 | padding: 0 15px;
48 | background: #2d2d2d; /* browsers that don't support rgba */
49 | *background-color: transparent;
50 | background: rgba(45, 45, 45, .35);
51 | -moz-border-radius: 6px;
52 | -webkit-border-radius: 6px;
53 | border-radius: 6px;
54 | border: 1px solid #3d3d3d; /* browsers that don't support rgba */
55 | border: 1px solid rgba(255, 255, 255, .15);
56 | -moz-box-shadow: 0 2px 3px 0 rgba(0, 0, 0, .1) inset;
57 | -webkit-box-shadow: 0 2px 3px 0 rgba(0, 0, 0, .1) inset;
58 | box-shadow: 0 2px 3px 0 rgba(0, 0, 0, .1) inset;
59 | font-family: 'PT Sans', Helvetica, Arial, sans-serif;
60 | font-size: 14px;
61 | color: #fff;
62 | text-shadow: 0 1px 2px rgba(0, 0, 0, .1);
63 | -o-transition: all .2s;
64 | -moz-transition: all .2s;
65 | -webkit-transition: all .2s;
66 | -ms-transition: all .2s;
67 | background-repeat: no-repeat;
68 | background-position: 5%;
69 | text-indent: 12%;
70 | }
71 |
72 | .userName {
73 | background-size: 20px;
74 | background-image: url(img/user.png);
75 | }
76 |
77 | .password {
78 | background-size: 20px;
79 | background-image: url(img/key.png);
80 | }
81 |
82 | #verify-icon {
83 | position: absolute;
84 | width: 30px;
85 | height: 42px;
86 | background-position: right;
87 | background-size: 70%;
88 | background-repeat: no-repeat;
89 | background-image: url(img/verify.png);
90 | left: .5em;
91 | top: 38%;
92 | }
93 |
94 | .verify {
95 | background-position: right;
96 | background-image: url(/getVerify;123456789);
97 | }
98 |
99 | .verify.onX {
100 | cursor: pointer;
101 | }
102 |
103 | input:-moz-placeholder {
104 | color: #e6e6e6;
105 | text-indent: 12%;
106 | }
107 |
108 | input:-ms-input-placeholder {
109 | color: #e6e6e6;
110 | text-indent: 12%;
111 | }
112 |
113 | input::-webkit-input-placeholder {
114 | color: #e6e6e6;
115 | text-indent: 12%;
116 | }
117 |
118 | input:focus {
119 | outline: none;
120 | -moz-box-shadow: 0 2px 3px 0 rgba(0, 0, 0, .1) inset, 0 2px 7px 0
121 | rgba(0, 0, 0, .2);
122 | -webkit-box-shadow: 0 2px 3px 0 rgba(0, 0, 0, .1) inset, 0 2px 7px 0
123 | rgba(0, 0, 0, .2);
124 | box-shadow: 0 2px 3px 0 rgba(0, 0, 0, .1) inset, 0 2px 7px 0
125 | rgba(0, 0, 0, .2);
126 | }
127 |
128 | button {
129 | cursor: pointer;
130 | width: 300px;
131 | height: 44px;
132 | margin-top: 25px;
133 | padding: 0;
134 | background: #009688;
135 | -moz-border-radius: 6px;
136 | -webkit-border-radius: 6px;
137 | border-radius: 6px;
138 | border: 0px;
139 | -moz-box-shadow: 0 15px 30px 0 rgba(255, 255, 255, .25) inset, 0 2px 7px
140 | 0 rgba(0, 0, 0, .2);
141 | -webkit-box-shadow: 0 15px 30px 0 rgba(255, 255, 255, .25) inset, 0 2px
142 | 7px 0 rgba(0, 0, 0, .2);
143 | box-shadow: 0 15px 30px 0 rgba(255, 255, 255, .25) inset, 0 2px 7px 0
144 | rgba(0, 0, 0, .2);
145 | font-family: 'PT Sans', Helvetica, Arial, sans-serif;
146 | font-size: 14px;
147 | font-weight: 700;
148 | color: #fff;
149 | text-shadow: 0 1px 2px rgba(0, 0, 0, .1);
150 | -o-transition: all .2s;
151 | -moz-transition: all .2s;
152 | -webkit-transition: all .2s;
153 | -ms-transition: all .2s;
154 | }
155 |
156 | button:hover {
157 | -moz-box-shadow: 0 15px 30px 0 rgba(255, 255, 255, .15) inset, 0 2px 7px
158 | 0 rgba(0, 0, 0, .2);
159 | -webkit-box-shadow: 0 15px 30px 0 rgba(255, 255, 255, .15) inset, 0 2px
160 | 7px 0 rgba(0, 0, 0, .2);
161 | box-shadow: 0 15px 30px 0 rgba(255, 255, 255, .15) inset, 0 2px 7px 0
162 | rgba(0, 0, 0, .2);
163 | }
164 |
165 | button:active {
166 | -moz-box-shadow: 0 15px 30px 0 rgba(255, 255, 255, .15) inset, 0 2px 7px
167 | 0 rgba(0, 0, 0, .2);
168 | -webkit-box-shadow: 0 15px 30px 0 rgba(255, 255, 255, .15) inset, 0 2px
169 | 7px 0 rgba(0, 0, 0, .2);
170 | box-shadow: 0 5px 8px 0 rgba(0, 0, 0, .1) inset, 0 1px 4px 0
171 | rgba(0, 0, 0, .1);
172 | border: 0px solid #ef4300;
173 | }
174 |
175 | .famous {
176 | width: 60%;
177 | margin: 50px auto 0 auto;
178 | font-size: 14px;
179 | text-shadow: 0 1px 3px rgba(0, 0, 0, .2);
180 | }
181 |
182 | .famous p {
183 | position: relative;
184 | left: -140%;
185 | color: white;
186 | top: 0
187 | }
--------------------------------------------------------------------------------
/src/main/java/com/chao/cloud/im/controller/LayimController.java:
--------------------------------------------------------------------------------
1 | package com.chao.cloud.im.controller;
2 |
3 | import java.util.Collections;
4 | import java.util.List;
5 | import java.util.Map;
6 | import java.util.Set;
7 | import java.util.stream.Collectors;
8 |
9 | import javax.servlet.http.HttpSession;
10 | import javax.validation.Valid;
11 | import javax.validation.constraints.NotNull;
12 |
13 | import org.springframework.beans.factory.annotation.Autowired;
14 | import org.springframework.stereotype.Controller;
15 | import org.springframework.ui.Model;
16 | import org.springframework.validation.annotation.Validated;
17 | import org.springframework.web.bind.annotation.PathVariable;
18 | import org.springframework.web.bind.annotation.RequestMapping;
19 | import org.springframework.web.bind.annotation.ResponseBody;
20 |
21 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
22 | import com.baomidou.mybatisplus.core.toolkit.Wrappers;
23 | import com.baomidou.mybatisplus.extension.api.R;
24 | import com.chao.cloud.im.ai.constant.AiConstant;
25 | import com.chao.cloud.im.dal.entity.ImGroupUser;
26 | import com.chao.cloud.im.dal.entity.ImMsgHis;
27 | import com.chao.cloud.im.dal.entity.ImUser;
28 | import com.chao.cloud.im.domain.dto.LoginDTO;
29 | import com.chao.cloud.im.domain.vo.MsgHisVO;
30 | import com.chao.cloud.im.service.ImGroupService;
31 | import com.chao.cloud.im.service.ImGroupUserService;
32 | import com.chao.cloud.im.service.ImMsgHisService;
33 | import com.chao.cloud.im.service.ImUserService;
34 | import com.chao.cloud.im.websocket.LayimChatWebSocket;
35 | import com.chao.cloud.im.websocket.constant.ImConstant;
36 | import com.chao.cloud.im.websocket.model.LayimModel;
37 | import com.chao.cloud.im.websocket.model.LayimModel.Friend;
38 | import com.chao.cloud.im.websocket.model.LayimModel.Group;
39 | import com.chao.cloud.im.websocket.model.LayimModel.Member;
40 | import com.chao.cloud.im.websocket.model.LayimModel.User;
41 |
42 | import cn.hutool.core.collection.CollUtil;
43 | import cn.hutool.core.util.StrUtil;
44 |
45 | @RequestMapping("layim")
46 | @Controller
47 | @Validated
48 | public class LayimController extends BaseController {
49 |
50 | private final String friendKeyTemplate = "{}@{}";
51 | @Autowired
52 | private ImUserService imUserService;
53 | @Autowired
54 | private ImGroupService imGroupService;
55 | @Autowired
56 | private ImGroupUserService imGroupUserService;
57 | @Autowired
58 | private LayimChatWebSocket singleChatWebSocket;
59 | @Autowired
60 | private ImMsgHisService imMsgHisService;
61 |
62 | @RequestMapping("/page/{html}")
63 | public String html(@PathVariable("html") String html) {
64 | return "layim/" + html;
65 | }
66 |
67 | @RequestMapping("/chatlog")
68 | public String chatlog(@Valid MsgHisVO vo, HttpSession session, Model m) {
69 | // 获取当前用户
70 | LoginDTO user = getUser(session);
71 | // 去重
72 | Set fromTo = CollUtil.newHashSet(ImMsgHisService.fromTo(user.getId(), vo.getId()),
73 | ImMsgHisService.fromTo(vo.getId(), user.getId()));
74 | // 构造条件
75 | LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery();
76 | queryWrapper.in(ImMsgHis::getFromTo, fromTo);
77 | queryWrapper.eq(ImMsgHis::getType, vo.getType());
78 | // 获取数据总数
79 | int count = imMsgHisService.count(queryWrapper);
80 | m.addAttribute("count", count);
81 | return "layim/chatlog";
82 | }
83 |
84 | /**
85 | * 初始化参数
86 | * @param html
87 | * @return
88 | */
89 | @RequestMapping("/initData")
90 | @ResponseBody
91 | public R initData(HttpSession session) {
92 | LayimModel model = new LayimModel();
93 | // 获取自己-好友列表-群组列表
94 | User mine = LayimModel.userConvert(getUser(session));
95 | mine.setStatus(ImConstant.LineStatus.ON);// 在线
96 | // mine
97 | model.setMine(mine);
98 | // friend
99 | List list = imUserService.list();
100 | // 分组->归类
101 | if (CollUtil.isNotEmpty(list)) {
102 | Map> friendMap = list.stream().collect(
103 | Collectors.groupingBy(iu -> StrUtil.format(friendKeyTemplate, iu.getDeptId(), iu.getDeptName())));
104 | Set onlineKey = singleChatWebSocket.onlineKey();
105 | // 设置在线状态
106 | List friends = CollUtil.newArrayList();
107 | friendMap.forEach((k, v) -> {
108 | String[] dept = k.split("@");
109 | Friend friend = new LayimModel.Friend();
110 | friend.setId(Integer.valueOf(dept[0]));
111 | friend.setGroupname(dept[1]);
112 | List users = v.stream().map(u -> {
113 | User user = LayimModel.userConvert(u);
114 | user.setStatus(AiConstant.REBOT_ID_LIST.contains(u.getId()) || onlineKey.contains(u.getId())
115 | ? ImConstant.LineStatus.ON
116 | : ImConstant.LineStatus.OFF);
117 | return user;
118 | }).collect(Collectors.toList());
119 | friend.setList(users);
120 | friends.add(friend);
121 | });
122 | model.setFriend(friends);
123 | }
124 | // group
125 | List groups = imGroupService.list().stream().map(g -> {
126 | Group group = new LayimModel.Group();
127 | group.setId(g.getId());
128 | group.setGroupname(g.getNickName());
129 | group.setAvatar(g.getHeadImg());
130 | return group;
131 | }).collect(Collectors.toList());
132 | model.setGroup(groups);
133 | return R.ok(model);
134 | }
135 |
136 | /**
137 | * 群成员
138 | * @param id 群id
139 | * @return
140 | */
141 | @RequestMapping("/members")
142 | @ResponseBody
143 | public R memebers(@NotNull Integer id) {
144 | // 根据群id 查询群下的会员
145 | List groupUsers = imGroupUserService
146 | .list(Wrappers.lambdaQuery().eq(ImGroupUser::getGroupId, id));
147 | List list = Collections.emptyList();
148 | if (CollUtil.isNotEmpty(groupUsers)) {
149 | List userIds = groupUsers.stream().map(ImGroupUser::getUserId).collect(Collectors.toList());
150 | list = imUserService.list(Wrappers.lambdaQuery().in(ImUser::getId, userIds)).stream()
151 | .map(LayimModel::userConvert).collect(Collectors.toList());
152 | }
153 | return R.ok(Member.builder().list(list).build());
154 | }
155 |
156 | }
--------------------------------------------------------------------------------
/src/main/resources/templates/layim/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | layim
5 |
6 | 下线
7 |
8 |
9 |
10 | (AI): im 智能聊天历史进程
11 |
12 |
13 |
14 |
15 |
16 |
2019-07-01,整合腾讯/百度AI,实现智能机器人对话......
17 |
18 |
19 |
20 |
21 |
22 |
2019-06-20,历史消息,离线消息记录
23 |
24 |
25 |
26 |
27 |
28 |
2019-06-01,图片,文件上传
29 |
30 |
31 |
32 |
33 |
34 |
2019-05-20,集成websocket
35 |
36 |
37 |
38 |
39 |
40 |
2019-05-10,集成layim 前端框架
41 |
42 |
43 |
44 |
45 |
46 |
2019-04-20,im 首个版本发布
47 |
48 |
49 |
50 |
51 |
52 |
2019-04-10,im 孵化
53 |
54 |
55 |
56 |
57 |
58 |
2019-04-05,创立im项目
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
190 |
191 |
--------------------------------------------------------------------------------
/src/main/resources/static/layui/layui.js:
--------------------------------------------------------------------------------
1 | /** layui-v2.4.5 MIT License By https://www.layui.com */
2 | ;!function(e){"use strict";var t=document,o={modules:{},status:{},timeout:10,event:{}},n=function(){this.v="2.4.5"},r=function(){var e=t.currentScript?t.currentScript.src:function(){for(var e,o=t.scripts,n=o.length-1,r=n;r>0;r--)if("interactive"===o[r].readyState){e=o[r].src;break}return e||o[n].src}();return e.substring(0,e.lastIndexOf("/")+1)}(),i=function(t){e.console&&console.error&&console.error("Layui hint: "+t)},a="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),u={layer:"modules/layer",laydate:"modules/laydate",laypage:"modules/laypage",laytpl:"modules/laytpl",layim:"modules/layim",layedit:"modules/layedit",form:"modules/form",upload:"modules/upload",tree:"modules/tree",table:"modules/table",element:"modules/element",rate:"modules/rate",colorpicker:"modules/colorpicker",slider:"modules/slider",carousel:"modules/carousel",flow:"modules/flow",util:"modules/util",code:"modules/code",jquery:"modules/jquery",mobile:"modules/mobile","layui.all":"../layui.all"};n.prototype.cache=o,n.prototype.define=function(e,t){var n=this,r="function"==typeof e,i=function(){var e=function(e,t){layui[e]=t,o.status[e]=!0};return"function"==typeof t&&t(function(n,r){e(n,r),o.callback[n]=function(){t(e)}}),this};return r&&(t=e,e=[]),layui["layui.all"]||!layui["layui.all"]&&layui["layui.mobile"]?i.call(n):(n.use(e,i),n)},n.prototype.use=function(e,n,l){function s(e,t){var n="PLaySTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/;("load"===e.type||n.test((e.currentTarget||e.srcElement).readyState))&&(o.modules[f]=t,d.removeChild(v),function r(){return++m>1e3*o.timeout/4?i(f+" is not a valid module"):void(o.status[f]?c():setTimeout(r,4))}())}function c(){l.push(layui[f]),e.length>1?y.use(e.slice(1),n,l):"function"==typeof n&&n.apply(layui,l)}var y=this,p=o.dir=o.dir?o.dir:r,d=t.getElementsByTagName("head")[0];e="string"==typeof e?[e]:e,window.jQuery&&jQuery.fn.on&&(y.each(e,function(t,o){"jquery"===o&&e.splice(t,1)}),layui.jquery=layui.$=jQuery);var f=e[0],m=0;if(l=l||[],o.host=o.host||(p.match(/\/\/([\s\S]+?)\//)||["//"+location.host+"/"])[0],0===e.length||layui["layui.all"]&&u[f]||!layui["layui.all"]&&layui["layui.mobile"]&&u[f])return c(),y;if(o.modules[f])!function g(){return++m>1e3*o.timeout/4?i(f+" is not a valid module"):void("string"==typeof o.modules[f]&&o.status[f]?c():setTimeout(g,4))}();else{var v=t.createElement("script"),h=(u[f]?p+"lay/":/^\{\/\}/.test(y.modules[f])?"":o.base||"")+(y.modules[f]||f)+".js";h=h.replace(/^\{\/\}/,""),v.async=!0,v.charset="utf-8",v.src=h+function(){var e=o.version===!0?o.v||(new Date).getTime():o.version||"";return e?"?v="+e:""}(),d.appendChild(v),!v.attachEvent||v.attachEvent.toString&&v.attachEvent.toString().indexOf("[native code")<0||a?v.addEventListener("load",function(e){s(e,h)},!1):v.attachEvent("onreadystatechange",function(e){s(e,h)}),o.modules[f]=h}return y},n.prototype.getStyle=function(t,o){var n=t.currentStyle?t.currentStyle:e.getComputedStyle(t,null);return n[n.getPropertyValue?"getPropertyValue":"getAttribute"](o)},n.prototype.link=function(e,n,r){var a=this,u=t.createElement("link"),l=t.getElementsByTagName("head")[0];"string"==typeof n&&(r=n);var s=(r||e).replace(/\.|\//g,""),c=u.id="layuicss-"+s,y=0;return u.rel="stylesheet",u.href=e+(o.debug?"?v="+(new Date).getTime():""),u.media="all",t.getElementById(c)||l.appendChild(u),"function"!=typeof n?a:(function p(){return++y>1e3*o.timeout/100?i(e+" timeout"):void(1989===parseInt(a.getStyle(t.getElementById(c),"width"))?function(){n()}():setTimeout(p,100))}(),a)},o.callback={},n.prototype.factory=function(e){if(layui[e])return"function"==typeof o.callback[e]?o.callback[e]:null},n.prototype.addcss=function(e,t,n){return layui.link(o.dir+"css/"+e,t,n)},n.prototype.img=function(e,t,o){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,"function"==typeof t&&t(n)},void(n.onerror=function(e){n.onerror=null,"function"==typeof o&&o(e)}))},n.prototype.config=function(e){e=e||{};for(var t in e)o[t]=e[t];return this},n.prototype.modules=function(){var e={};for(var t in u)e[t]=u[t];return e}(),n.prototype.extend=function(e){var t=this;e=e||{};for(var o in e)t[o]||t.modules[o]?i("模块名 "+o+" 已被占用"):t.modules[o]=e[o];return t},n.prototype.router=function(e){var t=this,e=e||location.hash,o={path:[],search:{},hash:(e.match(/[^#](#.*$)/)||[])[1]||""};return/^#\//.test(e)?(e=e.replace(/^#\//,""),o.href="/"+e,e=e.replace(/([^#])(#.*$)/,"$1").split("/")||[],t.each(e,function(e,t){/^\w+=/.test(t)?function(){t=t.split("="),o.search[t[0]]=t[1]}():o.path.push(t)}),o):o},n.prototype.data=function(t,o,n){if(t=t||"layui",n=n||localStorage,e.JSON&&e.JSON.parse){if(null===o)return delete n[t];o="object"==typeof o?o:{key:o};try{var r=JSON.parse(n[t])}catch(i){var r={}}return"value"in o&&(r[o.key]=o.value),o.remove&&delete r[o.key],n[t]=JSON.stringify(r),o.key?r[o.key]:r}},n.prototype.sessionData=function(e,t){return this.data(e,t,sessionStorage)},n.prototype.device=function(t){var o=navigator.userAgent.toLowerCase(),n=function(e){var t=new RegExp(e+"/([^\\s\\_\\-]+)");return e=(o.match(t)||[])[1],e||!1},r={os:function(){return/windows/.test(o)?"windows":/linux/.test(o)?"linux":/iphone|ipod|ipad|ios/.test(o)?"ios":/mac/.test(o)?"mac":void 0}(),ie:function(){return!!(e.ActiveXObject||"ActiveXObject"in e)&&((o.match(/msie\s(\d+)/)||[])[1]||"11")}(),weixin:n("micromessenger")};return t&&!r[t]&&(r[t]=n(t)),r.android=/android/.test(o),r.ios="ios"===r.os,r},n.prototype.hint=function(){return{error:i}},n.prototype.each=function(e,t){var o,n=this;if("function"!=typeof t)return n;if(e=e||[],e.constructor===Object){for(o in e)if(t.call(e[o],o,e[o]))break}else for(o=0;oi?1:r"].join("")),o=t.elem.next();(o.hasClass(u)||o.hasClass(c))&&o.remove(),a.ie&&a.ie<10&&t.elem.wrap('
'),e.isFile()?(e.elemFile=t.elem,t.field=t.elem[0].name):t.elem.after(n),a.ie&&a.ie<10&&e.initIE()},p.prototype.initIE=function(){var e=this,t=e.config,n=i(''),a=i(['"].join(""));i("#"+f)[0]||i("body").append(n),t.elem.next().hasClass(c)||(e.elemFile.wrap(a),t.elem.next("."+c).append(function(){var e=[];return layui.each(t.data,function(i,t){t="function"==typeof t?t():t,e.push(' ')}),e.join("")}()))},p.prototype.msg=function(e){return t.msg(e,{icon:2,shift:6})},p.prototype.isFile=function(){var e=this.config.elem[0];if(e)return"input"===e.tagName.toLocaleLowerCase()&&"file"===e.type},p.prototype.preview=function(e){var i=this;window.FileReader&&layui.each(i.chooseFiles,function(i,t){var n=new FileReader;n.readAsDataURL(t),n.onload=function(){e&&e(i,t,this.result)}})},p.prototype.upload=function(e,t){var n,o=this,l=o.config,r=o.elemFile[0],u=function(){var t=0,n=0,a=e||o.files||o.chooseFiles||r.files,u=function(){l.multiple&&t+n===o.fileLength&&"function"==typeof l.allDone&&l.allDone({total:o.fileLength,successful:t,aborted:n})};layui.each(a,function(e,a){var r=new FormData;r.append(l.field,a),layui.each(l.data,function(e,i){i="function"==typeof i?i():i,r.append(e,i)}),i.ajax({url:l.url,type:"post",data:r,contentType:!1,processData:!1,dataType:"json",headers:l.headers||{},success:function(i){t++,d(e,i),u()},error:function(){n++,o.msg("请求上传接口出现异常"),m(e),u()}})})},c=function(){var e=i("#"+f);o.elemFile.parent().submit(),clearInterval(p.timer),p.timer=setInterval(function(){var i,t=e.contents().find("body");try{i=t.text()}catch(n){o.msg("获取上传后的响应信息出现异常"),clearInterval(p.timer),m()}i&&(clearInterval(p.timer),t.html(""),d(0,i))},30)},d=function(e,i){if(o.elemFile.next("."+s).remove(),r.value="","object"!=typeof i)try{i=JSON.parse(i)}catch(t){return i={},o.msg("请对上传接口返回有效JSON")}"function"==typeof l.done&&l.done(i,e||0,function(e){o.upload(e)})},m=function(e){l.auto&&(r.value=""),"function"==typeof l.error&&l.error(e||0,function(e){o.upload(e)})},h=l.exts,v=function(){var i=[];return layui.each(e||o.chooseFiles,function(e,t){i.push(t.name)}),i}(),g={preview:function(e){o.preview(e)},upload:function(e,i){var t={};t[e]=i,o.upload(t)},pushFile:function(){return o.files=o.files||{},layui.each(o.chooseFiles,function(e,i){o.files[e]=i}),o.files},resetFile:function(e,i,t){var n=new File([i],t);o.files=o.files||{},o.files[e]=n}},y=function(){if("choose"!==t&&!l.auto||(l.choose&&l.choose(g),"choose"!==t))return l.before&&l.before(g),a.ie?a.ie>9?u():c():void u()};if(v=0===v.length?r.value.match(/[^\/\\]+\..+/g)||[]||"":v,0!==v.length){switch(l.accept){case"file":if(h&&!RegExp("\\w\\.("+h+")$","i").test(escape(v)))return o.msg("选择的文件中包含不支持的格式"),r.value="";break;case"video":if(!RegExp("\\w\\.("+(h||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(v)))return o.msg("选择的视频中包含不支持的格式"),r.value="";break;case"audio":if(!RegExp("\\w\\.("+(h||"mp3|wav|mid")+")$","i").test(escape(v)))return o.msg("选择的音频中包含不支持的格式"),r.value="";break;default:if(layui.each(v,function(e,i){RegExp("\\w\\.("+(h||"jpg|png|gif|bmp|jpeg$")+")","i").test(escape(i))||(n=!0)}),n)return o.msg("选择的图片中包含不支持的格式"),r.value=""}if(o.fileLength=function(){var i=0,t=e||o.files||o.chooseFiles||r.files;return layui.each(t,function(){i++}),i}(),l.number&&o.fileLength>l.number)return o.msg("同时最多只能上传的数量为:"+l.number);if(l.size>0&&!(a.ie&&a.ie<10)){var F;if(layui.each(o.chooseFiles,function(e,i){if(i.size>1024*l.size){var t=l.size/1024;t=t>=1?t.toFixed(2)+"MB":l.size+"KB",r.value="",F=t}}),F)return o.msg("文件不能超过"+F)}y()}},p.prototype.events=function(){var e=this,t=e.config,o=function(i){e.chooseFiles={},layui.each(i,function(i,t){var n=(new Date).getTime();e.chooseFiles[n+"-"+i]=t})},l=function(i,n){var a=e.elemFile,o=i.length>1?i.length+"个文件":(i[0]||{}).name||a[0].value.match(/[^\/\\]+\..+/g)||[]||"";a.next().hasClass(s)&&a.next().remove(),e.upload(null,"choose"),e.isFile()||t.choose||a.after(''+o+" ")};t.elem.off("upload.start").on("upload.start",function(){var a=i(this),o=a.attr("lay-data");if(o)try{o=new Function("return "+o)(),e.config=i.extend({},t,o)}catch(l){n.error("Upload element property lay-data configuration item has a syntax error: "+o)}e.config.item=a,e.elemFile[0].click()}),a.ie&&a.ie<10||t.elem.off("upload.over").on("upload.over",function(){var e=i(this);e.attr("lay-over","")}).off("upload.leave").on("upload.leave",function(){var e=i(this);e.removeAttr("lay-over")}).off("upload.drop").on("upload.drop",function(n,a){var r=i(this),u=a.originalEvent.dataTransfer.files||[];r.removeAttr("lay-over"),o(u),t.auto?e.upload(u):l(u)}),e.elemFile.off("upload.change").on("upload.change",function(){var i=this.files||[];o(i),t.auto?e.upload():l(i)}),t.bindAction.off("upload.action").on("upload.action",function(){e.upload()}),t.elem.data("haveEvents")||(e.elemFile.on("change",function(){i(this).trigger("upload.change")}),t.elem.on("click",function(){e.isFile()||i(this).trigger("upload.start")}),t.drag&&t.elem.on("dragover",function(e){e.preventDefault(),i(this).trigger("upload.over")}).on("dragleave",function(e){i(this).trigger("upload.leave")}).on("drop",function(e){e.preventDefault(),i(this).trigger("upload.drop",e)}),t.bindAction.on("click",function(){i(this).trigger("upload.action")}),t.elem.data("haveEvents",!0))},o.render=function(e){var i=new p(e);return l.call(i)},e(r,o)});
--------------------------------------------------------------------------------
/src/main/resources/static/layui/lay/modules/slider.js:
--------------------------------------------------------------------------------
1 | /** layui-v2.4.5 MIT License By https://www.layui.com */
2 | ;layui.define("jquery",function(e){"use strict";var i=layui.jquery,t={config:{},index:layui.slider?layui.slider.index+1e4:0,set:function(e){var t=this;return t.config=i.extend({},t.config,e),t},on:function(e,i){return layui.onevent.call(this,n,e,i)}},a=function(){var e=this,i=e.config;return{setValue:function(i,t){return e.slide("set",i,t||0)},config:i}},n="slider",l="layui-disabled",s="layui-slider",r="layui-slider-bar",o="layui-slider-wrap",u="layui-slider-wrap-btn",d="layui-slider-tips",v="layui-slider-input",c="layui-slider-input-txt",m="layui-slider-input-btn",p="layui-slider-hover",f=function(e){var a=this;a.index=++t.index,a.config=i.extend({},a.config,t.config,e),a.render()};f.prototype.config={type:"default",min:0,max:100,value:0,step:1,showstep:!1,tips:!0,input:!1,range:!1,height:200,disabled:!1,theme:"#009688"},f.prototype.render=function(){var e=this,t=e.config;if(t.step<1&&(t.step=1),t.maxt.min?a:t.min,t.value[1]=n>t.min?n:t.min,t.value[0]=t.value[0]>t.max?t.max:t.value[0],t.value[1]=t.value[1]>t.max?t.max:t.value[1];var r=Math.floor((t.value[0]-t.min)/(t.max-t.min)*100),v=Math.floor((t.value[1]-t.min)/(t.max-t.min)*100),m=v-r+"%";r+="%",v+="%"}else{"object"==typeof t.value&&(t.value=Math.min.apply(null,t.value)),t.valuet.max&&(t.value=t.max);var m=Math.floor((t.value-t.min)/(t.max-t.min)*100)+"%"}var p=t.disabled?"#c2c2c2":t.theme,f=''+(t.tips?'
':"")+'
'+(t.range?'
':"")+"
",h=i(t.elem),y=h.next("."+s);if(y[0]&&y.remove(),e.elemTemp=i(f),t.range?(e.elemTemp.find("."+o).eq(0).data("value",t.value[0]),e.elemTemp.find("."+o).eq(1).data("value",t.value[1])):e.elemTemp.find("."+o).data("value",t.value),h.html(e.elemTemp),"vertical"===t.type&&e.elemTemp.height(t.height+"px"),t.showstep){for(var g=(t.max-t.min)/t.step,b="",x=1;x')}e.elemTemp.append(b)}if(t.input&&!t.range){var w=i('');h.css("position","relative"),h.append(w),h.find("."+c).children("input").val(t.value),"vertical"===t.type?w.css({left:0,top:-48}):e.elemTemp.css("margin-right",w.outerWidth()+15)}t.disabled?(e.elemTemp.addClass(l),e.elemTemp.find("."+u).addClass(l)):e.slide(),e.elemTemp.find("."+u).on("mouseover",function(){var a="vertical"===t.type?t.height:e.elemTemp[0].offsetWidth,n=e.elemTemp.find("."+o),l="vertical"===t.type?a-i(this).parent()[0].offsetTop-n.height():i(this).parent()[0].offsetLeft,s=l/a*100,r=i(this).parent().data("value"),u=t.setTips?t.setTips(r):r;e.elemTemp.find("."+d).html(u),"vertical"===t.type?e.elemTemp.find("."+d).css({bottom:s+"%","margin-bottom":"20px",display:"inline-block"}):e.elemTemp.find("."+d).css({left:s+"%",display:"inline-block"})}).on("mouseout",function(){e.elemTemp.find("."+d).css("display","none")})},f.prototype.slide=function(e,t,a){var n=this,l=n.config,s=n.elemTemp,f=function(){return"vertical"===l.type?l.height:s[0].offsetWidth},h=s.find("."+o),y=s.next("."+v),g=y.children("."+c).children("input").val(),b=100/((l.max-l.min)/Math.ceil(l.step)),x=function(e,i){e=Math.ceil(e)*b>100?Math.ceil(e)*b:Math.round(e)*b,e=e>100?100:e,h.eq(i).css("vertical"===l.type?"bottom":"left",e+"%");var t=T(h[0].offsetLeft),a=l.range?T(h[1].offsetLeft):0;"vertical"===l.type?(s.find("."+d).css({bottom:e+"%","margin-bottom":"20px"}),t=T(f()-h[0].offsetTop-h.height()),a=l.range?T(f()-h[1].offsetTop-h.height()):0):s.find("."+d).css("left",e+"%"),t=t>100?100:t,a=a>100?100:a;var n=Math.min(t,a),o=Math.abs(t-a);"vertical"===l.type?s.find("."+r).css({height:o+"%",bottom:n+"%"}):s.find("."+r).css({width:o+"%",left:n+"%"});var u=l.min+Math.round((l.max-l.min)*e/100);if(g=u,y.children("."+c).children("input").val(g),h.eq(i).data("value",u),u=l.setTips?l.setTips(u):u,s.find("."+d).html(u),l.range){var v=[h.eq(0).data("value"),h.eq(1).data("value")];v[0]>v[1]&&v.reverse()}l.change&&l.change(l.range?v:u)},T=function(e){var i=e/f()*100/b,t=Math.round(i)*b;return e==f()&&(t=Math.ceil(i)*b),t},w=i(['
f()&&(r=f());var o=r/f()*100/b;x(o,e),t.addClass(p),s.find("."+d).show(),i.preventDefault()},o=function(){t.removeClass(p),s.find("."+d).hide()};M(r,o)})}),s.on("click",function(e){var t=i("."+u);if(!t.is(event.target)&&0===t.has(event.target).length&&t.length){var a,n="vertical"===l.type?f()-e.clientY+i(this).offset().top:e.clientX-i(this).offset().left;n<0&&(n=0),n>f()&&(n=f());var s=n/f()*100/b;a=l.range?"vertical"===l.type?Math.abs(n-parseInt(i(h[0]).css("bottom")))>Math.abs(n-parseInt(i(h[1]).css("bottom")))?1:0:Math.abs(n-h[0].offsetLeft)>Math.abs(n-h[1].offsetLeft)?1:0:0,x(s,a),e.preventDefault()}}),y.hover(function(){var e=i(this);e.children("."+m).fadeIn("fast")},function(){var e=i(this);e.children("."+m).fadeOut("fast")}),y.children("."+m).children("i").each(function(e){i(this).on("click",function(){g=1==e?g-l.stepl.max?l.max:Number(g)+l.step;var i=(g-l.min)/(l.max-l.min)*100/b;x(i,0)})});var q=function(){var e=this.value;e=isNaN(e)?0:e,e=el.max?l.max:e,this.value=e;var i=(e-l.min)/(l.max-l.min)*100/b;x(i,0)};y.children("."+c).children("input").on("keydown",function(e){13===e.keyCode&&(e.preventDefault(),q.call(this))}).on("change",q)},f.prototype.events=function(){var e=this;e.config},t.render=function(e){var i=new f(e);return a.call(i)},e(n,t)});
--------------------------------------------------------------------------------
/src/main/resources/static/layui/css/modules/layim/html/msgbox.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 消息盒子
9 |
10 |
11 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
注意:这些都是模拟数据,实际使用时,需将其中的模拟接口改为你的项目真实接口。
32 | 该模版文件所在目录(相对于layui.js):/css/modules/layim/html/msgbox.html
33 |
34 |
35 |
62 |
63 |
66 |
67 |
68 |
69 |
206 |
207 |
208 |
--------------------------------------------------------------------------------
/src/main/resources/static/layui/lay/modules/element.js:
--------------------------------------------------------------------------------
1 | /** layui-v2.4.5 MIT License By https://www.layui.com */
2 | ;layui.define("jquery",function(t){"use strict";var a=layui.$,i=(layui.hint(),layui.device()),e="element",l="layui-this",n="layui-show",s=function(){this.config={}};s.prototype.set=function(t){var i=this;return a.extend(!0,i.config,t),i},s.prototype.on=function(t,a){return layui.onevent.call(this,e,t,a)},s.prototype.tabAdd=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.children(".layui-tab-bar"),o=l.children(".layui-tab-content"),r='"+(i.title||"unnaming")+" ";return s[0]?s.before(r):n.append(r),o.append(''+(i.content||"")+"
"),f.hideTabMore(!0),f.tabAuto(),this},s.prototype.tabDelete=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabDelete(null,s),this},s.prototype.tabChange=function(t,i){var e=".layui-tab-title",l=a(".layui-tab[lay-filter="+t+"]"),n=l.children(e),s=n.find('>li[lay-id="'+i+'"]');return f.tabClick.call(s[0],null,null,s),this},s.prototype.tab=function(t){t=t||{},b.on("click",t.headerElem,function(i){var e=a(this).index();f.tabClick.call(this,i,e,null,t)})},s.prototype.progress=function(t,i){var e="layui-progress",l=a("."+e+"[lay-filter="+t+"]"),n=l.find("."+e+"-bar"),s=n.find("."+e+"-text");return n.css("width",i),s.text(i),this};var o=".layui-nav",r="layui-nav-item",c="layui-nav-bar",u="layui-nav-tree",d="layui-nav-child",y="layui-nav-more",h="layui-anim layui-anim-upbit",f={tabClick:function(t,i,s,o){o=o||{};var r=s||a(this),i=i||r.parent().children("li").index(r),c=o.headerElem?r.parent():r.parents(".layui-tab").eq(0),u=o.bodyElem?a(o.bodyElem):c.children(".layui-tab-content").children(".layui-tab-item"),d=r.find("a"),y=c.attr("lay-filter");"javascript:;"!==d.attr("href")&&"_blank"===d.attr("target")||(r.addClass(l).siblings().removeClass(l),u.eq(i).addClass(n).siblings().removeClass(n)),layui.event.call(this,e,"tab("+y+")",{elem:c,index:i})},tabDelete:function(t,i){var n=i||a(this).parent(),s=n.index(),o=n.parents(".layui-tab").eq(0),r=o.children(".layui-tab-content").children(".layui-tab-item"),c=o.attr("lay-filter");n.hasClass(l)&&(n.next()[0]?f.tabClick.call(n.next()[0],null,s+1):n.prev()[0]&&f.tabClick.call(n.prev()[0],null,s-1)),n.remove(),r.eq(s).remove(),setTimeout(function(){f.tabAuto()},50),layui.event.call(this,e,"tabDelete("+c+")",{elem:o,index:s})},tabAuto:function(){var t="layui-tab-more",e="layui-tab-bar",l="layui-tab-close",n=this;a(".layui-tab").each(function(){var s=a(this),o=s.children(".layui-tab-title"),r=(s.children(".layui-tab-content").children(".layui-tab-item"),'lay-stope="tabmore"'),c=a(' ');if(n===window&&8!=i.ie&&f.hideTabMore(!0),s.attr("lay-allowClose")&&o.find("li").each(function(){var t=a(this);if(!t.find("."+l)[0]){var i=a('ဆ ');i.on("click",f.tabDelete),t.append(i)}}),"string"!=typeof s.attr("lay-unauto"))if(o.prop("scrollWidth")>o.outerWidth()+1){if(o.find("."+e)[0])return;o.append(c),s.attr("overflow",""),c.on("click",function(a){o[this.title?"removeClass":"addClass"](t),this.title=this.title?"":"收缩"})}else o.find("."+e).remove(),s.removeAttr("overflow")})},hideTabMore:function(t){var i=a(".layui-tab-title");t!==!0&&"tabmore"===a(t.target).attr("lay-stope")||(i.removeClass("layui-tab-more"),i.find(".layui-tab-bar").attr("title",""))},clickThis:function(){var t=a(this),i=t.parents(o),n=i.attr("lay-filter"),s=t.parent(),c=t.siblings("."+d),y="string"==typeof s.attr("lay-unselect");"javascript:;"!==t.attr("href")&&"_blank"===t.attr("target")||y||c[0]||(i.find("."+l).removeClass(l),s.addClass(l)),i.hasClass(u)&&(c.removeClass(h),c[0]&&(s["none"===c.css("display")?"addClass":"removeClass"](r+"ed"),"all"===i.attr("lay-shrink")&&s.siblings().removeClass(r+"ed"))),layui.event.call(this,e,"nav("+n+")",t)},collapse:function(){var t=a(this),i=t.find(".layui-colla-icon"),l=t.siblings(".layui-colla-content"),s=t.parents(".layui-collapse").eq(0),o=s.attr("lay-filter"),r="none"===l.css("display");if("string"==typeof s.attr("lay-accordion")){var c=s.children(".layui-colla-item").children("."+n);c.siblings(".layui-colla-title").children(".layui-colla-icon").html(""),c.removeClass(n)}l[r?"addClass":"removeClass"](n),i.html(r?"":""),layui.event.call(this,e,"collapse("+o+")",{title:t,content:l,show:r})}};s.prototype.init=function(t,e){var l=function(){return e?'[lay-filter="'+e+'"]':""}(),s={tab:function(){f.tabAuto.call({})},nav:function(){var t=200,e={},s={},p={},b=function(l,o,r){var c=a(this),f=c.find("."+d);o.hasClass(u)?l.css({top:c.position().top,height:c.children("a").outerHeight(),opacity:1}):(f.addClass(h),l.css({left:c.position().left+parseFloat(c.css("marginLeft")),top:c.position().top+c.height()-l.height()}),e[r]=setTimeout(function(){l.css({width:c.width(),opacity:1})},i.ie&&i.ie<10?0:t),clearTimeout(p[r]),"block"===f.css("display")&&clearTimeout(s[r]),s[r]=setTimeout(function(){f.addClass(n),c.find("."+y).addClass(y+"d")},300))};a(o+l).each(function(i){var l=a(this),o=a(' '),h=l.find("."+r);l.find("."+c)[0]||(l.append(o),h.on("mouseenter",function(){b.call(this,o,l,i)}).on("mouseleave",function(){l.hasClass(u)||(clearTimeout(s[i]),s[i]=setTimeout(function(){l.find("."+d).removeClass(n),l.find("."+y).removeClass(y+"d")},300))}),l.on("mouseleave",function(){clearTimeout(e[i]),p[i]=setTimeout(function(){l.hasClass(u)?o.css({height:0,top:o.position().top+o.height()/2,opacity:0}):o.css({width:0,left:o.position().left+o.width()/2,opacity:0})},t)})),h.find("a").each(function(){var t=a(this),i=(t.parent(),t.siblings("."+d));i[0]&&!t.children("."+y)[0]&&t.append(' '),t.off("click",f.clickThis).on("click",f.clickThis)})})},breadcrumb:function(){var t=".layui-breadcrumb";a(t+l).each(function(){var t=a(this),i="lay-separator",e=t.attr(i)||"/",l=t.find("a");l.next("span["+i+"]")[0]||(l.each(function(t){t!==l.length-1&&a(this).after(""+e+" ")}),t.css("visibility","visible"))})},progress:function(){var t="layui-progress";a("."+t+l).each(function(){var i=a(this),e=i.find(".layui-progress-bar"),l=e.attr("lay-percent");e.css("width",function(){return/^.+\/.+$/.test(l)?100*new Function("return "+l)()+"%":l}()),i.attr("lay-showPercent")&&setTimeout(function(){e.html(''+l+" ")},350)})},collapse:function(){var t="layui-collapse";a("."+t+l).each(function(){var t=a(this).find(".layui-colla-item");t.each(function(){var t=a(this),i=t.find(".layui-colla-title"),e=t.find(".layui-colla-content"),l="none"===e.css("display");i.find(".layui-colla-icon").remove(),i.append(''+(l?"":"")+" "),i.off("click",f.collapse).on("click",f.collapse)})})}};return s[t]?s[t]():layui.each(s,function(t,a){a()})},s.prototype.render=s.prototype.init;var p=new s,b=a(document);p.render();var v=".layui-tab-title li";b.on("click",v,f.tabClick),b.on("click",f.hideTabMore),a(window).on("resize",f.tabAuto),t(e,p)});
--------------------------------------------------------------------------------
/src/main/resources/static/layui/css/modules/laydate/default/laydate.css:
--------------------------------------------------------------------------------
1 | /** layui-v2.4.5 MIT License By https://www.layui.com */
2 | .laydate-set-ym,.layui-laydate,.layui-laydate *,.layui-laydate-list{box-sizing:border-box}html #layuicss-laydate{display:none;position:absolute;width:1989px}.layui-laydate *{margin:0;padding:0}.layui-laydate{position:absolute;z-index:66666666;margin:5px 0;border-radius:2px;font-size:14px;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-name:laydate-upbit;animation-name:laydate-upbit}.layui-laydate-main{width:272px}.layui-laydate-content td,.layui-laydate-header *,.layui-laydate-list li{transition-duration:.3s;-webkit-transition-duration:.3s}@-webkit-keyframes laydate-upbit{from{-webkit-transform:translate3d(0,20px,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes laydate-upbit{from{transform:translate3d(0,20px,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-laydate-static{position:relative;z-index:0;display:inline-block;margin:0;-webkit-animation:none;animation:none}.laydate-ym-show .laydate-next-m,.laydate-ym-show .laydate-prev-m{display:none!important}.laydate-ym-show .laydate-next-y,.laydate-ym-show .laydate-prev-y{display:inline-block!important}.laydate-time-show .laydate-set-ym span[lay-type=month],.laydate-time-show .laydate-set-ym span[lay-type=year],.laydate-time-show .layui-laydate-header .layui-icon,.laydate-ym-show .laydate-set-ym span[lay-type=month]{display:none!important}.layui-laydate-header{position:relative;line-height:30px;padding:10px 70px 5px}.laydate-set-ym span,.layui-laydate-header i{padding:0 5px;cursor:pointer}.layui-laydate-header *{display:inline-block;vertical-align:bottom}.layui-laydate-header i{position:absolute;top:10px;color:#999;font-size:18px}.layui-laydate-header i.laydate-prev-y{left:15px}.layui-laydate-header i.laydate-prev-m{left:45px}.layui-laydate-header i.laydate-next-y{right:15px}.layui-laydate-header i.laydate-next-m{right:45px}.laydate-set-ym{width:100%;text-align:center;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.laydate-time-text{cursor:default!important}.layui-laydate-content{position:relative;padding:10px;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.layui-laydate-content table{border-collapse:collapse;border-spacing:0}.layui-laydate-content td,.layui-laydate-content th{width:36px;height:30px;padding:5px;text-align:center}.layui-laydate-content td{position:relative;cursor:pointer}.laydate-day-mark{position:absolute;left:0;top:0;width:100%;height:100%;line-height:30px;font-size:12px;overflow:hidden}.laydate-day-mark::after{position:absolute;content:'';right:2px;top:2px;width:5px;height:5px;border-radius:50%}.layui-laydate-footer{position:relative;height:46px;line-height:26px;padding:10px 20px}.layui-laydate-footer span{margin-right:15px;display:inline-block;cursor:pointer;font-size:12px}.layui-laydate-footer span:hover{color:#5FB878}.laydate-footer-btns{position:absolute;right:10px;top:10px}.laydate-footer-btns span{height:26px;line-height:26px;margin:0 0 0 -1px;padding:0 10px;border:1px solid #C9C9C9;background-color:#fff;white-space:nowrap;vertical-align:top;border-radius:2px}.layui-laydate-list>li,.layui-laydate-range .layui-laydate-main{display:inline-block;vertical-align:middle}.layui-laydate-list{position:absolute;left:0;top:0;width:100%;height:100%;padding:10px;background-color:#fff}.layui-laydate-list>li{position:relative;width:33.3%;height:36px;line-height:36px;margin:3px 0;text-align:center;cursor:pointer}.laydate-month-list>li{width:25%;margin:17px 0}.laydate-time-list>li{height:100%;margin:0;line-height:normal;cursor:default}.laydate-time-list p{position:relative;top:-4px;line-height:29px}.laydate-time-list ol{height:181px;overflow:hidden}.laydate-time-list>li:hover ol{overflow-y:auto}.laydate-time-list ol li{width:130%;padding-left:33px;line-height:30px;text-align:left;cursor:pointer}.layui-laydate-hint{position:absolute;top:115px;left:50%;width:250px;margin-left:-125px;line-height:20px;padding:15px;text-align:center;font-size:12px}.layui-laydate-range{width:546px}.layui-laydate-range .laydate-main-list-0 .laydate-next-m,.layui-laydate-range .laydate-main-list-0 .laydate-next-y,.layui-laydate-range .laydate-main-list-1 .laydate-prev-m,.layui-laydate-range .laydate-main-list-1 .laydate-prev-y{display:none}.layui-laydate-range .laydate-main-list-1 .layui-laydate-content{border-left:1px solid #e2e2e2}.layui-laydate,.layui-laydate-hint{border:1px solid #d2d2d2;box-shadow:0 2px 4px rgba(0,0,0,.12);background-color:#fff;color:#666}.layui-laydate-header{border-bottom:1px solid #e2e2e2}.layui-laydate-header i:hover,.layui-laydate-header span:hover{color:#5FB878}.layui-laydate-content{border-top:none 0;border-bottom:none 0}.layui-laydate-content th{font-weight:400;color:#333}.layui-laydate-content td{color:#666}.layui-laydate-content td.laydate-selected{background-color:#00F7DE}.laydate-selected:hover{background-color:#00F7DE!important}.layui-laydate-content td:hover,.layui-laydate-list li:hover{background-color:#eaeaea;color:#333}.laydate-time-list li ol{margin:0;padding:0;border:1px solid #e2e2e2;border-left-width:0}.laydate-time-list li:first-child ol{border-left-width:1px}.laydate-time-list>li:hover{background:0 0}.layui-laydate-content .laydate-day-next,.layui-laydate-content .laydate-day-prev{color:#d2d2d2}.laydate-selected.laydate-day-next,.laydate-selected.laydate-day-prev{background-color:#f8f8f8!important}.layui-laydate-footer{border-top:1px solid #e2e2e2}.layui-laydate-hint{color:#FF5722}.laydate-day-mark::after{background-color:#5FB878}.layui-laydate-content td.layui-this .laydate-day-mark::after{display:none}.layui-laydate-footer span[lay-type=date]{color:#5FB878}.layui-laydate .layui-this{background-color:#009688!important;color:#fff!important}.layui-laydate .laydate-disabled,.layui-laydate .laydate-disabled:hover{background:0 0!important;color:#d2d2d2!important;cursor:not-allowed!important;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.laydate-theme-molv{border:none}.laydate-theme-molv.layui-laydate-range{width:548px}.laydate-theme-molv .layui-laydate-main{width:274px}.laydate-theme-molv .layui-laydate-header{border:none;background-color:#009688}.laydate-theme-molv .layui-laydate-header i,.laydate-theme-molv .layui-laydate-header span{color:#f6f6f6}.laydate-theme-molv .layui-laydate-header i:hover,.laydate-theme-molv .layui-laydate-header span:hover{color:#fff}.laydate-theme-molv .layui-laydate-content{border:1px solid #e2e2e2;border-top:none;border-bottom:none}.laydate-theme-molv .laydate-main-list-1 .layui-laydate-content{border-left:none}.laydate-theme-grid .laydate-month-list>li,.laydate-theme-grid .laydate-year-list>li,.laydate-theme-grid .layui-laydate-content td,.laydate-theme-grid .layui-laydate-content thead,.laydate-theme-molv .layui-laydate-footer{border:1px solid #e2e2e2}.laydate-theme-grid .laydate-selected,.laydate-theme-grid .laydate-selected:hover{background-color:#f2f2f2!important;color:#009688!important}.laydate-theme-grid .laydate-selected.laydate-day-next,.laydate-theme-grid .laydate-selected.laydate-day-prev{color:#d2d2d2!important}.laydate-theme-grid .laydate-month-list,.laydate-theme-grid .laydate-year-list{margin:1px 0 0 1px}.laydate-theme-grid .laydate-month-list>li,.laydate-theme-grid .laydate-year-list>li{margin:0 -1px -1px 0}.laydate-theme-grid .laydate-year-list>li{height:43px;line-height:43px}.laydate-theme-grid .laydate-month-list>li{height:71px;line-height:71px}
--------------------------------------------------------------------------------