master
15004070936 3 weeks ago
parent 19c32c2c80
commit 6112c7f639

@ -4,7 +4,10 @@
<facet type="web" name="Web">
<configuration>
<webroots />
<sourceRoots />
<sourceRoots>
<root url="file://$MODULE_DIR$/src/main/java" />
<root url="file://$MODULE_DIR$/src/main/resources" />
</sourceRoots>
</configuration>
</facet>
<facet type="Spring" name="Spring">
@ -15,13 +18,13 @@
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
@ -99,6 +102,9 @@
<orderEntry type="library" name="Maven: io.lettuce:lettuce-core:6.1.8.RELEASE" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-common:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-handler:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-transport:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.projectreactor:reactor-core:3.4.18" level="project" />
<orderEntry type="library" name="Maven: org.reactivestreams:reactive-streams:1.0.3" level="project" />
@ -119,40 +125,13 @@
<orderEntry type="library" name="Maven: com.beust:jcommander:1.82" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: com.github.jai-imageio:jai-imageio-core:1.4.0" level="project" />
<orderEntry type="library" name="Maven: org.projectlombok:lombok:1.18.24" level="project" />
<orderEntry type="library" name="Maven: cn.jpush.api:jiguang-common:1.1.7" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.60" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcpkix-jdk15on:1.60" level="project" />
<orderEntry type="library" name="Maven: cn.jpush.api:jpush-client:3.4.3" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-all:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec-dns:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec-haproxy:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec-http:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec-http2:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec-memcache:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec-mqtt:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec-redis:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec-smtp:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec-socks:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec-stomp:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec-xml:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-handler-proxy:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-resolver:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-resolver-dns:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-transport-rxtx:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-transport-sctp:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-transport-udt:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-transport-classes-epoll:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-transport-native-unix-common:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-transport-classes-kqueue:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-resolver-dns-classes-macos:4.1.77.Final" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: io.netty:netty-transport-native-epoll:linux-x86_64:4.1.77.Final" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: io.netty:netty-transport-native-epoll:linux-aarch_64:4.1.77.Final" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: io.netty:netty-transport-native-kqueue:osx-x86_64:4.1.77.Final" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: io.netty:netty-transport-native-kqueue:osx-aarch_64:4.1.77.Final" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: io.netty:netty-resolver-dns-native-macos:osx-x86_64:4.1.77.Final" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: io.netty:netty-resolver-dns-native-macos:osx-aarch_64:4.1.77.Final" level="project" />
<orderEntry type="library" name="Maven: io.github.jpush:jiguang-sdk:5.3.0" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign:feign-core:13.1" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form:3.8.0" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign:feign-slf4j:13.1" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign:feign-okhttp:13.1" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign:feign-jackson:13.1" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15to18:1.78" level="project" />
<orderEntry type="library" name="Maven: io.springfox:springfox-boot-starter:3.0.0" level="project" />
<orderEntry type="library" name="Maven: io.springfox:springfox-oas:3.0.0" level="project" />
<orderEntry type="library" name="Maven: io.swagger.core.v3:swagger-annotations:2.1.2" level="project" />

@ -37,7 +37,7 @@ public class AppreciateController {
public RestResponse getList(@RequestBody Map map){
Long visitStore=Long.parseLong(map.get("visitStore").toString());
List<YjAppreciate> list= service.list(new QueryWrapper<YjAppreciate>()
.select("id,title,image,read_num,start_time")
.select("id,title,image,read_num,start_time,modify_time")
.eq("dept_id",visitStore)
.eq("status",true)
.orderByDesc("modify_time"));

@ -38,7 +38,7 @@ public class HealthyController {
public RestResponse getList(@RequestBody Map map){
Long visitStore=Long.parseLong(map.get("visitStore").toString());
List<YjHealthy> list= service.list(new QueryWrapper<YjHealthy>()
.select("id,title,image,read_num,start_time")
.select("id,title,image,read_num,start_time,modify_time")
.eq("dept_id",visitStore)
.eq("status",true)
.orderByDesc("modify_time"));

@ -37,7 +37,7 @@ public class InheritController {
public RestResponse getList(@RequestBody Map map){
Long visitStore=Long.parseLong(map.get("visitStore").toString());
List<YjInherit> list= inheritService.list(new QueryWrapper<YjInherit>()
.select("id,title,image,read_num,start_time")
.select("id,title,image,read_num,start_time,modify_time")
.eq("dept_id",visitStore)
.eq("status",true)
.orderByDesc("modify_time"));

@ -140,7 +140,7 @@ public class MyController extends BaseController {
*/
@PostMapping("/confirmClass")
public RestResponse confirmClass(@RequestBody Map map){
Long courseTimeId=(Long)map.get("courseTimeId");
Long courseTimeId=Long.parseLong(map.get("courseTimeId").toString());
String startTime=map.get("startTime").toString();//HH:mm:ss
String endTime=map.get("endTime").toString();//HH:mm:ss
if (StrUtil.hasEmpty(startTime,endTime)){

@ -38,7 +38,7 @@ public class PracticeMomentsController {
public RestResponse getList(@RequestBody Map map){
Long visitStore=Long.parseLong(map.get("visitStore").toString());
List<YjPracticeMoments> list=momentsService.list(new QueryWrapper<YjPracticeMoments>()
.select("id,title,image,read_num,start_time")
.select("id,title,image,read_num,start_time,modify_time")
.eq("dept_id",visitStore)
.eq("status",true)
.orderByDesc("modify_time"));

@ -36,7 +36,7 @@ public class SenseController {
public RestResponse getList(@RequestBody Map map){
Long visitStore=Long.parseLong(map.get("visitStore").toString());
List<YjSense> list= service.list(new QueryWrapper<YjSense>()
.select("id,title,image,read_num,start_time")
.select("id,title,image,read_num,start_time,modify_time")
.eq("dept_id",visitStore)
.eq("status",true)
.orderByDesc("modify_time"));

@ -92,7 +92,7 @@ public class CourseController extends BaseController {
*/
@PostMapping("/cancelCourse")
public RestResponse cancelCourse(@RequestBody Map map){
Long courseTimeId =(Long)map.get("courseTimeId");
Long courseTimeId =Long.parseLong(map.get("courseTimeId").toString());
return scClaTimeService.cancelCourse(courseTimeId);
}

@ -6,6 +6,7 @@ import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.domain.entity.AppUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.im.domain.vo.FriendVO;
import com.ruoyi.im.domain.vo.OnlineTerminalVO;
import com.ruoyi.im.domain.vo.UserVO;
import com.ruoyi.im.service.FriendService;
import com.ruoyi.web.controller.im.result.Result;
@ -53,12 +54,13 @@ public class FriendController {
@GetMapping("/find/{friendId}")
@Operation(summary = "查找好友信息", description = "查找好友信息")
public Result<FriendVO> findFriend(@NotNull(message = "好友id不可为空") @PathVariable Long friendId) {
public Result<FriendVO> findFriend(@NotNull(message = "好友id不可为空") @PathVariable String friendId) {
Long userId= SecurityUtils.getAppLoginUser().getAppUser().getId();
if (friendId.equals(userId)) {
Long fId=Long.valueOf(friendId);
if (fId.equals(userId)) {
return null;
}
return ResultUtils.success(friendService.findFriend(userId,friendId));
return ResultUtils.success(friendService.findFriend(userId,fId));
}
@ -68,5 +70,9 @@ public class FriendController {
friendService.delFriend(friendId);
return ResultUtils.success();
}
@GetMapping("/terminal/online")
@Operation(summary = "判断用户哪个终端在线", description = "返回在线的用户id的终端集合")
public Result<List<OnlineTerminalVO>> getOnlineTerminal(@NotNull @RequestParam("userIds") String userIds) {
return ResultUtils.success(friendService.getOnlineTerminals(userIds));
}
}

@ -1,6 +1,7 @@
package com.ruoyi.web.controller.im;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.im.domain.vo.GroupMemberVO;
import com.ruoyi.im.domain.vo.GroupVO;
import com.ruoyi.im.service.GroupService;
import com.ruoyi.web.controller.im.result.Result;
@ -42,6 +43,11 @@ public class GroupController {
return ResultUtils.success(groupService.findGroups());
}
@Operation(summary = "查询群聊成员", description = "查询群聊成员")
@GetMapping("/members/{groupId}")
public Result<List<GroupMemberVO>> findGroupMembers(
@NotNull(message = "群聊id不能为空") @PathVariable Long groupId) {
return ResultUtils.success(groupService.findGroupMembers(groupId));
}
}

@ -31,21 +31,21 @@ public class GroupMessageController {
@GetMapping("/maxReadedId")
@Operation(summary = "获取最大已读消息的id", description = "获取某个会话中已读消息的最大id")
public Result<String> getMaxReadedId(@RequestParam Long groupId) {
return ResultUtils.success(groupMessageService.getMaxReadedId(groupId),"成功");
public Result<String> getMaxReadedId(@RequestParam String groupId) {
return ResultUtils.success(groupMessageService.getMaxReadedId(Long.parseLong(groupId.toString())),"成功");
}
@GetMapping("/pullOfflineMessage")
@Operation(summary = "拉取离线消息", description = "拉取离线消息,消息将通过webscoket异步推送")
public Result pullOfflineMessage(@RequestParam Long minId) {
groupMessageService.pullOfflineMessage(minId);
public Result pullOfflineMessage(@RequestParam String minId) {
groupMessageService.pullOfflineMessage(Long.parseLong(minId.toString()));
return ResultUtils.success();
}
@PutMapping("/readed")
@Operation(summary = "消息已读", description = "将群聊中的消息状态置为已读")
public Result readedMessage(@RequestParam Long groupId) {
groupMessageService.readedMessage(groupId);
public Result readedMessage(@RequestParam String groupId) {
groupMessageService.readedMessage(Long.parseLong(groupId.toString()));
return ResultUtils.success();
}

@ -55,23 +55,23 @@ public class PrivateMessageController {
*/
@GetMapping("/pullOfflineMessage")
@Operation(summary = "拉取离线消息", description = "拉取离线消息,消息将通过webscoket异步推送")
public Result pullOfflineMessage(@RequestParam Long minId) {
privateMessageService.pullOfflineMessage(minId);
public Result pullOfflineMessage(@RequestParam String minId) {
privateMessageService.pullOfflineMessage(Long.parseLong(minId.toString()));
return ResultUtils.success();
}
@PutMapping("/readed")
@Operation(summary = "消息已读", description = "将会话中接收的消息状态置为已读")
public Result readedMessage(@RequestParam Long friendId) {
privateMessageService.readedMessage(friendId);
public Result readedMessage(@RequestParam String friendId) {
privateMessageService.readedMessage(Long.parseLong(friendId.toString()));
return ResultUtils.success();
}
@GetMapping("/maxReadedId")
@Operation(summary = "获取最大已读消息的id", description = "获取某个会话中已读消息的最大id")
public Result<Long> getMaxReadedId(@RequestParam Long friendId) {
return ResultUtils.success(privateMessageService.getMaxReadedId(friendId));
public Result<Long> getMaxReadedId(@RequestParam String friendId) {
return ResultUtils.success(privateMessageService.getMaxReadedId(Long.parseLong(friendId.toString())));
}
// @GetMapping("/history")

@ -1,6 +1,8 @@
package com.ruoyi.web.controller.login;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.im.util.BeanUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@ -12,6 +14,7 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.AppUser;
import com.ruoyi.common.core.domain.model.AppLoginUser;
import com.ruoyi.common.core.domain.model.AppUserLoginBody;
import com.ruoyi.common.utils.JpushClientUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.web.service.AppLoginService;
import com.ruoyi.basic.domain.YjStore;
@ -65,6 +68,7 @@ public class AppLoginController {
}
AppUser appUser=new AppUser();
appUser.setPhoneNumber(loginBody.getPhonenumber());
appUser.setNickName(loginBody.getPhonenumber());
appUser.setPassword(SecurityUtils.encryptPassword(loginBody.getPassword()));
appUser.setStatus(1);
appUserService.save(appUser);
@ -83,6 +87,17 @@ public class AppLoginController {
AjaxResult ajax = AjaxResult.success();
String token=appLoginService.login(loginBody.getPhonenumber(),loginBody.getPassword(),loginBody.getCode(),loginBody.getUuid(),loginBody.getRegisterId(),loginBody.getMiniAppCode());
ajax.put(Constants.TOKEN, token);
// 保存设备id绑定别名便于推送消息
if (StrUtil.isNotEmpty(loginBody.getRegisterId())){
appUserService.update(new UpdateWrapper<AppUser>().eq("phone_number",loginBody.getPhonenumber())
.set("register_id",loginBody.getRegisterId())
.set(StrUtil.isNotEmpty(loginBody.getLoginSysPlatform()),"login_sys_platform",loginBody.getLoginSysPlatform())
);
// 极光推送注册设备id绑定别名便于推送消息
JpushClientUtil.bindAlias(loginBody.getRegisterId(),loginBody.getPhonenumber());
}
return ajax;
}
@ -103,7 +118,8 @@ public class AppLoginController {
appUser.setPhonenumber(user.getAppUser().getPhoneNumber());
// 角色集合
appUser.setRoleId(user.getRoles());
//管理人员(教练、店长、顾问)默认访问门店
//管理人员(教练、店长、顾问)默认访问门店.
// if (ObjectUtil.isNotEmpty(user.getManageAccountId())&&ObjectUtil.isNotEmpty(appUser.getVisitStore())){
// Long deptId= userService.selectUserById(user.getManageAccountId()).getDeptId();
// appUser.setVisitStore(deptId);

@ -8,6 +8,7 @@ import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.JpushClientUtil;
import com.ruoyi.common.utils.sign.Base64;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.system.service.ISysConfigService;
@ -20,8 +21,7 @@ import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
@ -92,11 +92,31 @@ public class CaptchaController
{
return AjaxResult.error(e.getMessage());
}
testBindAlias();
ajax.put("uuid", uuid);
ajax.put("img", Base64.encode(os.toByteArray()));
return ajax;
}
public void testBindAlias() {
// JpushClientUtil.bindAlias("reg_123", "user_888");
// JpushClientUtil.pushToAll("瑜伽汇","这是java广播消息",null);
// JpushClientUtil.pushByRegistrationId(Arrays.asList("170976fa8bac0ade1e0"),"瑜伽汇","这是RegistrationId广播消息",null);
// JpushClientUtil.bindAlias("160a3797c9280401a82","15004070936");
// JpushClientUtil.pushToUser("15004070936","瑜伽汇","这是Alias消息",null);
// JpushClientUtil.pushToUsers(Arrays.asList("15004070936","mumu"),"瑜伽汇","瑜伽汇群组推送",null);
// String msg = String.format(
// "%s%s",
// "普拉提", "课程预约成功!"
// );
// Map<String, String> extras=new HashMap<>();
// extras.put("type","bookCourses");//type推送类型bookCourses 预约课程 groupMessage群聊消息 privateMessage私信消息
// extras.put("pagePath","/pages/book/book");//pagePath跳转页面
// extras.put("bookId","123456789");//bookId预约id
// JpushClientUtil.pushByRegistrationId(Arrays.asList("160a3797c9280401a82"),"瑜伽汇",msg,extras);
}
}

@ -146,3 +146,4 @@ xss:
wechat:
token: runpeng

@ -159,16 +159,15 @@
<!--极光推送-->
<dependency>
<groupId>cn.jpush.api</groupId>
<artifactId>jiguang-common</artifactId>
<version>1.1.7</version>
<groupId>io.github.jpush</groupId>
<artifactId>jiguang-sdk</artifactId>
<version>5.3.0</version>
</dependency>
<dependency>
<groupId>cn.jpush.api</groupId>
<artifactId>jpush-client</artifactId>
<version>3.4.3</version>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>
<!-- swagger3-->
<dependency>
<groupId>io.springfox</groupId>

@ -94,7 +94,12 @@ public class AppUser{
@TableField(exist = false)
private String newPassword;
//安卓android
//iOSios
//鸿蒙harmonyos
//微信小程序weixin
@TableField("login_sys_platform")
private String loginSysPlatform;

@ -1,5 +1,6 @@
package com.ruoyi.common.core.domain.model;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@ -66,4 +67,8 @@ public class AppUserLoginBody
*/
private String visitStoreId;
private String loginSysPlatform;
}

@ -7,8 +7,8 @@ import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@Component//尽量加上这个
@WebListener//声明为监听器 implements ServletContextListener
//@Component//尽量加上这个
//@WebListener//声明为监听器 implements ServletContextListener
public class SshContextListener implements ServletContextListener{
private SshTunnelConfig sshConnectionConfig;

@ -1,6 +1,8 @@
package com.ruoyi.common.im.model;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ruoyi.common.im.enums.IMTerminalType;
import lombok.Data;
@ -17,6 +19,7 @@ public class IMPrivateMessage<T> {
/**
* id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long recvId;

@ -1,14 +1,11 @@
package com.ruoyi.common.im.model;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author: Blue
* @date: 2023-09-24 09:23:11
* @version: 1.0
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ -17,6 +14,7 @@ public class IMUserInfo {
/**
* id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
/**

@ -1,591 +1,293 @@
package com.ruoyi.common.utils;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jiguang.common.resp.BooleanResult;
import cn.jiguang.common.resp.DefaultResult;
import cn.jpush.api.JPushClient;
import cn.jpush.api.device.TagAliasResult;
import cn.jpush.api.push.PushResult;
import cn.jpush.api.push.model.Message;
import cn.jpush.api.push.model.Options;
import cn.jpush.api.push.model.Platform;
import cn.jpush.api.push.model.PushPayload;
import cn.jpush.api.push.model.audience.Audience;
import cn.jpush.api.push.model.notification.AndroidNotification;
import cn.jpush.api.push.model.notification.IosNotification;
import cn.jpush.api.push.model.notification.Notification;
import cn.jpush.api.schedule.ScheduleResult;
import cn.hutool.core.bean.BeanUtil;
import cn.jiguang.sdk.api.DeviceApi;
import cn.jiguang.sdk.api.PushApi;
import cn.jiguang.sdk.bean.push.PushSendParam;
import cn.jiguang.sdk.bean.push.PushSendParam;
import cn.jiguang.sdk.bean.push.PushSendResult;
import cn.jiguang.sdk.exception.ApiErrorException;
import com.google.gson.Gson;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.*;
/**
*
* ( SDK 5.3.0)
* IM 线
*/
@Component
public class JpushClientUtil {
private static Logger logger = LoggerFactory.getLogger(JpushClientUtil.class);
/**
* app_keymasterSecret
*/
private final static String APPKER = "c2e673cd0e77c762ffe1a857";
private final static String MASTERSECRET = "34aace4d60d847468ab96097";
/**
* ApnsProduction
* apnsfalsetrueandroid
* 线 true
*/
private final static Boolean ApnsProduction = false;
private static JPushClient jPushClient = new JPushClient(MASTERSECRET,APPKER);
private static final Logger log = LoggerFactory.getLogger(JpushClientUtil.class);
private static final Gson gson = new Gson();
private static final String DEVICE_API_URL = "https://device.jpush.cn/v3/devices/";
private static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
private JpushClientUtil() {
// 静态变量,由 Spring 初始化
private static String appKey;
private static String masterSecret;
private static boolean apnsProduction;
private static PushApi pushApi;
throw new AssertionError("不能产生该对象");
}
private static OkHttpClient httpClient;
/**
*
*/
public static void testBindMobile() {
try {
DefaultResult result = jPushClient.bindMobile("100d855909a0c6d0655", "15004070936");
System.out.println("Got result " + result);
} catch (APIConnectionException e) {
logger.error("Connection error. Should retry later. ", e);
} catch (APIRequestException e) {
logger.error("Error response from JPush server. Should review and fix it. ", e);
logger.info("HTTP Status: " + e.getStatus());
logger.info("Error Code: " + e.getErrorCode());
logger.info("Error Message: " + e.getErrorMessage());
}
// 注入的属性
// @Value("${jpush.appKey}")
// private String injectedAppKey;
//
// @Value("${jpush.masterSecret}")
// private String injectedMasterSecret;
// private final static String injectedAppKey = "e75debc0daaed0a2a888d2f3";
// private final static String injectedMasterSecret = "e8728877c098e641cc6c51e5";
private final static String injectedAppKey = "d5edb270e4b1cc29616e1dd5";
private final static String injectedMasterSecret = "a49a9fd3df40747fb6e0d180";
// private final static String customAction = "com.test.java.sdk.";
// private final static String targetPackage = "com.test.java.sdk";
// private final static String targetActivity = "Activity 组件路径;";
// private final static String extrasParam = "S.key1=value;i.key2=3;";
// @Value("${jpush.apnsProduction:true}")
private final static boolean injectedApnsProduction = true;
@PostConstruct
public void init() {
// 将注入的值赋给静态变量
appKey = injectedAppKey;
masterSecret = injectedMasterSecret;
apnsProduction = injectedApnsProduction;
// 初始化 PushApi
pushApi = new PushApi.Builder()
.setAppKey(appKey)
.setMasterSecret(masterSecret)
.build();
// 初始化 OkHttpClient
httpClient = new OkHttpClient.Builder()
.connectTimeout(10, java.util.concurrent.TimeUnit.SECONDS)
.readTimeout(10, java.util.concurrent.TimeUnit.SECONDS)
.writeTimeout(10, java.util.concurrent.TimeUnit.SECONDS)
.build();
log.info("极光推送工具类初始化完成");
}
// ==================== 别名绑定方法 ====================
/**
* registrationId alias
* registrationId
*
* @param
* @return
*
* @param registrationId
* @param alias ID
*/
public static boolean bindAlias(String registerId,String phoneNumber) {
try {
DefaultResult defaultResult = jPushClient.updateDeviceTagAlias(registerId, phoneNumber, null,
null);
logger.info("Got result"+defaultResult);
} catch (APIConnectionException e) {
logger.error("Connection error. Should retry later. ", e);
e.printStackTrace();
} catch (APIRequestException e) {
logger.error("Error response from JPush server. Should review and fix it. ", e);
e.printStackTrace();
}finally {
jPushClient.close();
}
return false;
public static void bindAlias(String registrationId, String alias) {
if (registrationId == null || registrationId.isEmpty() || alias == null || alias.isEmpty()) {
log.warn("绑定别名失败: registrationId 或 alias 为空");
return;
}
public static BooleanResult hasMobile() {
BooleanResult deviceInTag = null;
try {
deviceInTag = jPushClient.isDeviceInTag(
String.valueOf(jPushClient.getDeviceTagAlias("100d855909a0c6d0655").alias), "100d855909a0c6d0655");
logger.info("Got result" + deviceInTag);
return deviceInTag ;
} catch (APIConnectionException e) {
e.printStackTrace();
} catch (APIRequestException e) {
e.printStackTrace();
}finally {
jPushClient.close();
// 构建请求体
Map<String, String> bodyMap = new HashMap<>();
bodyMap.put("alias", alias);
String jsonBody = gson.toJson(bodyMap);
// Basic 认证
String auth = Base64.getEncoder().encodeToString((appKey + ":" + masterSecret).getBytes());
RequestBody requestBody = RequestBody.create(JSON_MEDIA_TYPE,jsonBody);
Request request = new Request.Builder()
.url(DEVICE_API_URL + registrationId)
.addHeader("Authorization", "Basic " + auth)
.post(requestBody)
.build();
try (Response response = httpClient.newCall(request).execute()) {
if (response.isSuccessful()) {
log.info("别名绑定成功 | registrationId: {}, alias: {}", registrationId, alias);
} else {
// 极光 API 有时返回 404 但仍视为成功
String respBody = response.body() != null ? response.body().string() : "";
log.warn("别名绑定响应非成功,可能已成功 | registrationId: {}, alias: {}, code: {}, body: {}",
registrationId, alias, response.code(), respBody);
}
} catch (IOException e) {
log.error("别名绑定请求异常 | registrationId: {}, alias: {}, error: {}",
registrationId, alias, e.getMessage(), e);
}
return deviceInTag ;
}
public static void testGetDeviceTagAlias() {
try {
TagAliasResult result = jPushClient.getDeviceTagAlias("100d855909a0c6d0655");
logger.info(result.alias);
logger.info(result.tags.toString());
// ==================== 单聊推送:按别名 ====================
} catch (APIConnectionException e) {
logger.error("Connection error. Should retry later. ", e);
} catch (APIRequestException e) {
logger.error("Error response from JPush server. Should review and fix it. ", e);
logger.info("HTTP Status: " + e.getStatus());
logger.info("Error Code: " + e.getErrorCode());
logger.info("Error Message: " + e.getErrorMessage());
}
/**
* ( userId)
* @param alias
* @param title
* @param content
* @param extras ()
*/
public static void pushToUser(String alias, String title, String content, Map<String, String> extras) {
PushSendParam param = buildPushParamWithAlias(alias, title, content, extras);
sendPush(param, "user", alias);
}
// ==================== 群聊推送:按别名批量 ====================
/**
* ()
* @param alias ()
* @param notification_title
* @param msg_title
* @param msg_content
* @param extrasparam
* @return 01
* (线)
* @param aliases
* @param title
* @param content
* @param extras
*/
public static int sendToAlias( List<String> alias,String notification_title, String msg_title,
String msg_content, String extrasparam) {
JPushClient jPushClient = new JPushClient(MASTERSECRET,APPKER);
int result = 0;
try {
PushPayload pushPayload= JpushClientUtil.buildPushObject_all_alias_alertWithTitle(
alias,notification_title,msg_title,msg_content,extrasparam);
PushResult pushResult = jPushClient.sendPush(pushPayload);
if(pushResult.getResponseCode()==200){
result = 1;
}
logger.info("[极光推送]PushResult result is " + pushResult);
} catch (APIConnectionException e) {
logger.error("[极光推送]Connection error. Should retry later. ", e);
} catch (APIRequestException e) {
logger.error("[极光推送]Error response from JPush server. Should review and fix it. ", e);
logger.info("[极光推送]HTTP Status: " + e.getStatus());
logger.info("[极光推送]Error Code: " + e.getErrorCode());
logger.info("[极光推送]Error Message: " + e.getErrorMessage());
}
return result;
public static void pushToUsers(List<String> aliases, String title, String content, Map<String, String> extras) {
if (aliases == null || aliases.isEmpty()) return;
PushSendParam param = buildPushParamWithAliases(aliases, title, content, extras);
sendPush(param, "group-users", aliases.size() + " users");
}
// ==================== 全量推送 (谨慎使用) ====================
/**
* ()
* @param alias ()
* @param notification_title
* @param msg_title
* @param msg_content
* @param extrasparam
* @param time :yyyy-MM-dd HH:mm:ss
* @return 01
*
*/
public static int sendToAlias2( List<String> alias,String notification_title, String msg_title, String msg_content, String extrasparam, String time) {
JPushClient jPushClient = new JPushClient(MASTERSECRET,APPKER);
int result = 0;
try {
PushPayload pushPayload= JpushClientUtil.buildPushObject_all_alias_alertWithTitle(alias,notification_title,msg_title,msg_content,extrasparam);
ScheduleResult scheduleResult=jPushClient.createSingleSchedule("点呗生活", time, pushPayload);
if(scheduleResult.getResponseCode()==200){
result=1;
}
logger.info("[极光推送]ScheduleResult result is " + scheduleResult);
} catch (APIConnectionException e) {
logger.error("[极光推送]Connection error. Should retry later. ", e);
} catch (APIRequestException e) {
logger.error("[极光推送]Error response from JPush server. Should review and fix it. ", e);
logger.info("[极光推送]HTTP Status: " + e.getStatus());
logger.info("[极光推送]Error Code: " + e.getErrorCode());
logger.info("[极光推送]Error Message: " + e.getErrorMessage());
}
return result;
public static void pushToAll(String title, String content, Map<String, String> extras) {
PushSendParam param = buildPushParamWithAll(title, content, extras);
sendPush(param, "all", null);
}
// ==================== 根据 Registration ID 推送 (备用) ====================
/**
* RegistrationIds
* @param registrationIdList ID
* @param notification_title
* @param msg_title
* @param msg_content
* @param extrasparam
* @return 01
* Registration ID ()
*/
public static int sendToRegistrationIds( List<String> registrationIdList,String notification_title, String msg_title, String msg_content, String extrasparam) {
JPushClient jPushClient = new JPushClient(MASTERSECRET,APPKER);
int result = 0;
try {
PushPayload pushPayload= JpushClientUtil.buildPushObject_all_registrationIds_alertWithTitle(registrationIdList,notification_title,msg_title,msg_content,extrasparam);
PushResult pushResult=jPushClient.sendPush(pushPayload);
if(pushResult.getResponseCode()==200){
result=1;
}
logger.info("[极光推送]PushResult result is " + pushResult);
} catch (APIConnectionException e) {
logger.error("[极光推送]Connection error. Should retry later. ", e);
} catch (APIRequestException e) {
logger.error("[极光推送]Error response from JPush server. Should review and fix it. ", e);
logger.info("[极光推送]HTTP Status: " + e.getStatus());
logger.info("[极光推送]Error Code: " + e.getErrorCode());
logger.info("[极光推送]Error Message: " + e.getErrorMessage());
}
return result;
public static void pushByRegistrationId(List<String> regId, String title, String content, Map<String, String> extras) {
PushSendParam param = buildPushParamWithRegistrationId(regId, title, content, extras);
sendPush(param, "regId", regId.size()+ " users");
}
// public static void main(String[] agrs){
//
// List<String> strings=new ArrayList<>();
// strings.add("13065ffa4ee0d41a70c");
// String notification_title="通知内容标题";//通知内容标题
// String msg_title="消息内容标题";//消息内容标题
// String msg_content="消息内容";//消息内容
// String extrasparam="扩展字段";//扩展字段
// int i=sendToRegistrationIds(strings,notification_title,msg_title,msg_content,extrasparam);
// System.out.println(i==0?"0推送失败":"1推送成功");
// }
// ==================== 内部构建方法 (核心适配) ====================
/**
*
* @param notification_title
* @param msg_title
* @param msg_content
* @param extrasparam
* @return 01
*
*/
public static int sendToAllAndroid( String notification_title, String msg_title, String msg_content, String extrasparam) {
int result = 0;
try {
PushPayload pushPayload= JpushClientUtil.buildPushObject_android_all_alertWithTitle(notification_title,msg_title,msg_content,extrasparam);
PushResult pushResult=jPushClient.sendPush(pushPayload);
if(pushResult.getResponseCode()==200){
result=1;
}
logger.info("[极光推送]PushResult result is " + pushResult);
} catch (APIConnectionException e) {
logger.error("[极光推送]Connection error. Should retry later. ", e);
} catch (APIRequestException e) {
logger.error("[极光推送]Error response from JPush server. Should review and fix it. ", e);
logger.info("[极光推送]HTTP Status: " + e.getStatus());
logger.info("[极光推送]Error Code: " + e.getErrorCode());
logger.info("[极光推送]Error Message: " + e.getErrorMessage());
}
return result;
private static PushSendParam buildPushParamWithAlias(String alias, String title, String content, Map<String, String> extras) {
Map<String, Object> pushBody = buildCommonPushBody(title, content, extras);
Map map= new HashMap<>();
map.put("alias", Arrays.asList(alias));
pushBody.put("audience", map);
PushSendParam param = BeanUtil.toBean(pushBody, PushSendParam.class);
return param;
}
/**
* IOS
* @param notification_title
* @param msg_title
* @param msg_content
* @param extrasparam
* @return 01
*
*/
public static int sendToAllIos(String notification_title, String msg_title, String msg_content, String extrasparam) {
int result = 0;
try {
PushPayload pushPayload= JpushClientUtil.buildPushObject_ios_all_alertWithTitle(notification_title,msg_title,msg_content,extrasparam);
PushResult pushResult=jPushClient.sendPush(pushPayload);
if(pushResult.getResponseCode()==200){
result=1;
}
logger.info("[极光推送]PushResult result is " + pushResult);
} catch (APIConnectionException e) {
logger.error("[极光推送]Connection error. Should retry later. ", e);
} catch (APIRequestException e) {
logger.error("[极光推送]Error response from JPush server. Should review and fix it. ", e);
logger.info("[极光推送]HTTP Status: " + e.getStatus());
logger.info("[极光推送]Error Code: " + e.getErrorCode());
logger.info("[极光推送]Error Message: " + e.getErrorMessage());
}
return result;
private static PushSendParam buildPushParamWithAliases(List<String> aliases, String title, String content, Map<String, String> extras) {
Map<String, Object> pushBody = buildCommonPushBody(title, content, extras);
Map map= new HashMap<>();
map.put("alias", aliases);
pushBody.put("audience", map);
return BeanUtil.toBean(pushBody, PushSendParam.class);
}
/**
*
* @param notification_title
* @param msg_title
* @param msg_content
* @param extrasparam
* @return 01
*
*/
public static int sendToAll( String notification_title, String msg_title, String msg_content, String extrasparam) {
int result = 0;
try {
PushPayload pushPayload= JpushClientUtil.buildPushObject_android_and_ios(notification_title,msg_title,msg_content,extrasparam);
PushResult pushResult=jPushClient.sendPush(pushPayload);
if(pushResult.getResponseCode()==200){
result=1;
}
logger.info("[极光推送]PushResult result is " + pushResult);
} catch (APIConnectionException e) {
logger.error("[极光推送]Connection error. Should retry later. ", e);
} catch (APIRequestException e) {
logger.error("[极光推送]Error response from JPush server. Should review and fix it. ", e);
logger.info("[极光推送]HTTP Status: " + e.getStatus());
logger.info("[极光推送]Error Code: " + e.getErrorCode());
logger.info("[极光推送]Error Message: " + e.getErrorMessage());
}
return result;
private static PushSendParam buildPushParamWithAll(String title, String content, Map<String, String> extras) {
Map<String, Object> pushBody = buildCommonPushBody(title, content, extras);
pushBody.put("audience", "all");
return BeanUtil.toBean(pushBody, PushSendParam.class);
}
/**
* ()
* @param notification_title
* @param msg_title
* @param msg_content
* @param extrasparam
* @param time :yyyy-MM-dd HH:mm:ss
* @return 01
* Registration ID
*/
public static int sendToAll2( String notification_title, String msg_title, String msg_content, String extrasparam, String time) {
int result = 0;
try {
PushPayload pushPayload= JpushClientUtil.buildPushObject_android_and_ios(notification_title,msg_title,msg_content,extrasparam);
ScheduleResult scheduleResult=jPushClient.createSingleSchedule("测试", time, pushPayload);
if(scheduleResult.getResponseCode()==200){
result=1;
}
logger.info("[极光推送]scheduleResult result is " + scheduleResult);
} catch (APIConnectionException e) {
logger.error("[极光推送]Connection error. Should retry later. ", e);
} catch (APIRequestException e) {
logger.error("[极光推送]Error response from JPush server. Should review and fix it. ", e);
logger.info("[极光推送]HTTP Status: " + e.getStatus());
logger.info("[极光推送]Error Code: " + e.getErrorCode());
logger.info("[极光推送]Error Message: " + e.getErrorMessage());
}
return result;
private static PushSendParam buildPushParamWithRegistrationId(List<String> regId, String title, String content, Map<String, String> extras) {
Map<String, Object> pushBody = buildCommonPushBody(title, content, extras);
Map map= new HashMap<>();
map.put("registration_id", regId);
pushBody.put("audience", map);
return BeanUtil.toBean(pushBody, PushSendParam.class);
}
public static PushPayload buildPushObject_android_and_ios(String notification_title, String msg_title, String msg_content, String extrasparam) {
return PushPayload.newBuilder()
.setPlatform(Platform.android_ios())
.setAudience(Audience.all())
.setNotification(Notification.newBuilder()
.setAlert(msg_content)
.addPlatformNotification(AndroidNotification.newBuilder()
.setAlert(msg_content)
.setTitle(notification_title)
//此字段为透传字段不会显示在通知栏。用户可以通过此字段来做一些定制需求如特定的key传要指定跳转的页面value
.addExtra("url",extrasparam)
.build()
)
.addPlatformNotification(IosNotification.newBuilder()
//传一个IosAlert对象指定apns title、title、subtitle等
.setAlert(msg_content)
//直接传alert
//此项是指定此推送的badge自动加1
.incrBadge(1)
//此字段的值default表示系统默认声音传sound.caf表示此推送以项目里面打包的sound.caf声音来提醒
// 如果系统没有此音频则以系统默认声音提醒此字段如果传空字符串iOS9及以上的系统是无声音提醒以下的系统是默认声音
.setSound("sound.caf")
//此字段为透传字段不会显示在通知栏。用户可以通过此字段来做一些定制需求如特定的key传要指定跳转的页面value
.addExtra("url",extrasparam)
//此项说明此推送是一个background推送想了解background看http://docs.jpush.io/client/ios_tutorials/#ios-7-background-remote-notification
// .setContentAvailable(true)
.build()
)
.build()
)
//Platform指定了哪些平台就会像指定平台中符合推送条件的设备进行推送。 jpush的自定义消息
// sdk默认不做任何处理不会有通知提示。建议看文档http://docs.jpush.io/guideline/faq/的
// [通知与自定义消息有什么区别?]了解通知和自定义消息的区别
.setMessage(Message.newBuilder()
.setMsgContent(msg_content)
.setTitle(msg_title)
.addExtra("url",extrasparam)
.build())
.setOptions(Options.newBuilder()
//此字段的值是用来指定本推送要推送的apns环境false表示开发true表示生产对android和自定义消息无意义
.setApnsProduction(ApnsProduction)
//此字段是给开发者自己给推送编号,方便推送者分辨推送记录
.setSendno(1)
//此字段的值是用来指定本推送的离线保存时长,如果不传此字段则默认保存一天,最多指定保留十天,单位为秒
.setTimeToLive(86400)
.build()
)
.build();
}
/**
* body ()
*/
private static Map<String, Object> buildCommonPushBody(String title, String content, Map<String, String> extras) {
Map<String, Object> pushBody = new HashMap<>();
pushBody.put("platform", "all"); // 全平台
private static PushPayload buildPushObject_all_alias_alertWithTitle(List<String> alias,String notification_title, String msg_title, String msg_content, String extrasparam) {
//创建一个IosAlert对象可指定APNs的alert、title等字段
//IosAlert iosAlert = IosAlert.newBuilder().setTitleAndBody("title", "alert body").build();
return PushPayload.newBuilder()
//指定要推送的平台all代表当前应用配置了的所有平台也可以传android等具体平台
.setPlatform(Platform.all())
//指定推送的接收对象all代表所有人也可以指定已经设置成功的tag或alias或该应应用客户端调用接口获取到的registration id
.setAudience(Audience.alias(alias))
//.setAudience(Audience.registrationId(registrationId))
//jpush的通知android的由jpush直接下发iOS的由apns服务器下发Winphone的由mpns下发
.setNotification(Notification.newBuilder()
//指定当前推送的android通知
.addPlatformNotification(AndroidNotification.newBuilder()
.setAlert(msg_content)
.setTitle(notification_title)
//此字段为透传字段不会显示在通知栏。用户可以通过此字段来做一些定制需求如特定的key传要指定跳转的页面value
.addExtra("url",extrasparam)
.build())
//指定当前推送的iOS通知
.addPlatformNotification(IosNotification.newBuilder()
//传一个IosAlert对象指定apns title、title、subtitle等
.setAlert(msg_content)
//直接传alert
//此项是指定此推送的badge自动加1
.incrBadge(1)
//此字段的值default表示系统默认声音传sound.caf表示此推送以项目里面打包的sound.caf声音来提醒
// 如果系统没有此音频则以系统默认声音提醒此字段如果传空字符串iOS9及以上的系统是无声音提醒以下的系统是默认声音
.setSound("sound.caf")
//此字段为透传字段不会显示在通知栏。用户可以通过此字段来做一些定制需求如特定的key传要指定跳转的页面value
.addExtra("url",extrasparam)
//此项说明此推送是一个background推送想了解background看http://docs.jpush.io/client/ios_tutorials/#ios-7-background-remote-notification
//取消此注释消息推送时ios将无法在锁屏情况接收
// .setContentAvailable(true)
.build())
.build())
//Platform指定了哪些平台就会像指定平台中符合推送条件的设备进行推送。 jpush的自定义消息
// sdk默认不做任何处理不会有通知提示。建议看文档http://docs.jpush.io/guideline/faq/的
// [通知与自定义消息有什么区别?]了解通知和自定义消息的区别
.setMessage(Message.newBuilder()
.setMsgContent(msg_content)
.setTitle(msg_title)
.addExtra("url",extrasparam)
.build())
.setOptions(Options.newBuilder()
//此字段的值是用来指定本推送要推送的apns环境false表示开发true表示生产对android和自定义消息无意义
.setApnsProduction(ApnsProduction)
//此字段是给开发者自己给推送编号,方便推送者分辨推送记录
.setSendno(1)
//此字段的值是用来指定本推送的离线保存时长,如果不传此字段则默认保存一天,最多指定保留十天;
.setTimeToLive(86400)
.build())
.build();
}
// 构建通知体
Map<String, Object> notification = new HashMap<>();
notification.put("alert", content);
private static PushPayload buildPushObject_all_registrationIds_alertWithTitle(List<String> registrationIds,String notification_title, String msg_title, String msg_content, String extrasparam) {
//创建一个IosAlert对象可指定APNs的alert、title等字段
//IosAlert iosAlert = IosAlert.newBuilder().setTitleAndBody("title", "alert body").build();
return PushPayload.newBuilder()
//指定要推送的平台all代表当前应用配置了的所有平台也可以传android等具体平台
.setPlatform(Platform.all())
//指定推送的接收对象all代表所有人也可以指定已经设置成功的tag或alias或该应应用客户端调用接口获取到的registration id
.setAudience(Audience.registrationId(registrationIds))
//jpush的通知android的由jpush直接下发iOS的由apns服务器下发Winphone的由mpns下发
.setNotification(Notification.newBuilder()
//指定当前推送的android通知
.addPlatformNotification(AndroidNotification.newBuilder()
.setAlert(msg_content)
.setTitle(notification_title)
//此字段为透传字段不会显示在通知栏。用户可以通过此字段来做一些定制需求如特定的key传要指定跳转的页面value
.addExtra("url",extrasparam)
.build())
//指定当前推送的iOS通知
.addPlatformNotification(IosNotification.newBuilder()
//传一个IosAlert对象指定apns title、title、subtitle等
.setAlert(msg_content)
//直接传alert
//此项是指定此推送的badge自动加1
.incrBadge(1)
//此字段的值default表示系统默认声音传sound.caf表示此推送以项目里面打包的sound.caf声音来提醒
// 如果系统没有此音频则以系统默认声音提醒此字段如果传空字符串iOS9及以上的系统是无声音提醒以下的系统是默认声音
.setSound("sound.caf")
//此字段为透传字段不会显示在通知栏。用户可以通过此字段来做一些定制需求如特定的key传要指定跳转的页面value
.addExtra("url",extrasparam)
//此项说明此推送是一个background推送想了解background看http://docs.jpush.io/client/ios_tutorials/#ios-7-background-remote-notification
//取消此注释消息推送时ios将无法在锁屏情况接收
// .setContentAvailable(true)
.build())
.build())
//Platform指定了哪些平台就会像指定平台中符合推送条件的设备进行推送。 jpush的自定义消息
// sdk默认不做任何处理不会有通知提示。建议看文档http://docs.jpush.io/guideline/faq/的
// [通知与自定义消息有什么区别?]了解通知和自定义消息的区别
.setMessage(Message.newBuilder()
.setMsgContent(msg_content)
.setTitle(msg_title)
.addExtra("url",extrasparam)
.build())
.setOptions(Options.newBuilder()
//此字段的值是用来指定本推送要推送的apns环境false表示开发true表示生产对android和自定义消息无意义
.setApnsProduction(ApnsProduction)
//此字段是给开发者自己给推送编号,方便推送者分辨推送记录
.setSendno(1)
//此字段的值是用来指定本推送的离线保存时长,如果不传此字段则默认保存一天,最多指定保留十天;
.setTimeToLive(86400)
.build())
.build();
}
// Android 通知
Map<String, Object> android = new HashMap<>();
android.put("alert", content);
android.put("title", title);
// //点击消息跳转至
// Map<String, String> intent= new HashMap<>();
// String intentUrl = String.format(
// "intent:#Intent;action=%s;component=%s/%s;S.userId=%s;end",
// customAction, targetPackage, targetActivity, extrasParam
// );
// intent.put("url",intentUrl);
// android.put("intent",intent);
if (extras != null) {
android.put("extras", extras);
}
notification.put("android", android);
private static PushPayload buildPushObject_android_all_alertWithTitle(String notification_title, String msg_title, String msg_content, String extrasparam) {
List<String> rids=new ArrayList<>();
return PushPayload.newBuilder()
//指定要推送的平台all代表当前应用配置了的所有平台也可以传android等具体平台
.setPlatform(Platform.android())
//指定推送的接收对象all代表所有人也可以指定已经设置成功的tag或alias或该应应用客户端调用接口获取到的registration id
.setAudience(Audience.all())
//jpush的通知android的由jpush直接下发iOS的由apns服务器下发Winphone的由mpns下发
.setNotification(Notification.newBuilder()
//指定当前推送的android通知
.addPlatformNotification(AndroidNotification.newBuilder()
.setAlert(msg_content)
.setTitle(notification_title)
//此字段为透传字段不会显示在通知栏。用户可以通过此字段来做一些定制需求如特定的key传要指定跳转的页面value
.addExtra("url",extrasparam)
.build())
.build()
)
//Platform指定了哪些平台就会像指定平台中符合推送条件的设备进行推送。 jpush的自定义消息
// sdk默认不做任何处理不会有通知提示。建议看文档http://docs.jpush.io/guideline/faq/的
// [通知与自定义消息有什么区别?]了解通知和自定义消息的区别
.setMessage(Message.newBuilder()
.setMsgContent(msg_content)
.setTitle(msg_title)
.addExtra("url",extrasparam)
.build())
.setOptions(Options.newBuilder()
//此字段的值是用来指定本推送要推送的apns环境false表示开发true表示生产对android和自定义消息无意义
.setApnsProduction(ApnsProduction)
//此字段是给开发者自己给推送编号,方便推送者分辨推送记录
.setSendno(1)
//此字段的值是用来指定本推送的离线保存时长,如果不传此字段则默认保存一天,最多指定保留十天,单位为秒
.setTimeToLive(86400)
.build())
.build();
}
// iOS 通知
Map<String, Object> ios = new HashMap<>();
ios.put("alert", title);
ios.put("sound", "default");
ios.put("badge", "+1");
if (extras != null) {
ios.put("extras", extras);
}
notification.put("ios", ios);
private static PushPayload buildPushObject_ios_all_alertWithTitle( String notification_title, String msg_title, String msg_content, String extrasparam) {
return PushPayload.newBuilder()
//指定要推送的平台all代表当前应用配置了的所有平台也可以传android等具体平台
.setPlatform(Platform.ios())
//指定推送的接收对象all代表所有人也可以指定已经设置成功的tag或alias或该应应用客户端调用接口获取到的registration id
.setAudience(Audience.all())
//jpush的通知android的由jpush直接下发iOS的由apns服务器下发Winphone的由mpns下发
.setNotification(Notification.newBuilder()
//指定当前推送的android通知
.addPlatformNotification(IosNotification.newBuilder()
//传一个IosAlert对象指定apns title、title、subtitle等
.setAlert(msg_content)
//直接传alert
//此项是指定此推送的badge自动加1
.incrBadge(1)
//此字段的值default表示系统默认声音传sound.caf表示此推送以项目里面打包的sound.caf声音来提醒
// 如果系统没有此音频则以系统默认声音提醒此字段如果传空字符串iOS9及以上的系统是无声音提醒以下的系统是默认声音
.setSound("sound.caf")
//此字段为透传字段不会显示在通知栏。用户可以通过此字段来做一些定制需求如特定的key传要指定跳转的页面value
.addExtra("url",extrasparam)
//此项说明此推送是一个background推送想了解background看http://docs.jpush.io/client/ios_tutorials/#ios-7-background-remote-notification
// .setContentAvailable(true)
.build())
.build()
)
//Platform指定了哪些平台就会像指定平台中符合推送条件的设备进行推送。 jpush的自定义消息
// sdk默认不做任何处理不会有通知提示。建议看文档http://docs.jpush.io/guideline/faq/的
// [通知与自定义消息有什么区别?]了解通知和自定义消息的区别
.setMessage(Message.newBuilder()
.setMsgContent(msg_content)
.setTitle(msg_title)
.addExtra("url",extrasparam)
.build())
.setOptions(Options.newBuilder()
//此字段的值是用来指定本推送要推送的apns环境false表示开发true表示生产对android和自定义消息无意义
.setApnsProduction(ApnsProduction)
//此字段是给开发者自己给推送编号,方便推送者分辨推送记录
.setSendno(1)
//此字段的值是用来指定本推送的离线保存时长,如果不传此字段则默认保存一天,最多指定保留十天,单位为秒
.setTimeToLive(86400)
.build())
.build();
pushBody.put("notification", notification);
// 选项
Map<String, Object> options = new HashMap<>();
options.put("time_to_live", 86400);
options.put("apns_production", apnsProduction);
pushBody.put("options", options);
return pushBody;
}
public static Boolean sendMessage(String phone,String notification_title,String msg_content){
List<String> strings=new ArrayList<>();
strings.add(phone);
String msg_title="";//消息内容标题
String extrasparam="";//扩展字段
int i= sendToAlias(strings,notification_title,msg_title,msg_content,extrasparam);
System.out.println(i==0?"0推送失败":"1推送成功");
return i==0?false:true;
/**
*
*/
private static void sendPush(PushSendParam param, String type, String target) {
try {
// 调用 PushApi 的 send 方法进行推送
PushSendResult result = pushApi.send(param);
log.info("极光推送成功 | type: {} | target: {} | msgId: {}", type, target, result.getMessageId());
} catch (ApiErrorException e) {
// 处理 API 错误可获取错误码、HTTP状态码和限流信息
int httpStatus = e.getStats();
int errorCode = e.getApiError().getError().getCode();
String errorMessage = e.getApiError().getError().getMessage();
log.error("极光推送失败 | type: {} | target: {} | httpStatus: {} | errorCode: {} | error: {}",
type, target, httpStatus, errorCode, errorMessage, e);
// 此处可接入重试机制或报警
}
}

@ -8,6 +8,7 @@ import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.AppLoginUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.JpushClientUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.course.service.IScStudentService;
import com.ruoyi.system.service.SysTeacherService;
@ -84,6 +85,7 @@ public class AppUserDetailsServiceImpl implements UserDetailsService
SysUser user1=new SysUser();
user1.setPassword(user.getPassword());
passwordService.validate(user1);
return createLoginUser(user,roles.toArray(new Long[roles.size()]) ,studentId);
}

@ -11,6 +11,7 @@ import com.ruoyi.common.im.enums.IMSendCode;
import com.ruoyi.common.im.enums.IMTerminalType;
import com.ruoyi.common.im.model.*;
import com.ruoyi.common.im.mq.RedisMQTemplate;
import com.ruoyi.common.utils.JpushClientUtil;
import com.ruoyi.imclient.listener.MessageListenerMulticaster;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
@ -257,7 +258,7 @@ public class IMSender {
public Boolean isOnline(Long userId) {
String key = String.join(":", IMRedisKey.IM_USER_SERVER_ID, userId.toString(), "*");
return !Objects.requireNonNull(redisMQTemplate.keys(key)).isEmpty();
return !Objects.requireNonNull(redisMQTemplate.keys(key)).isEmpty();//null.isEmpty()->ture->!->false->! ->ture ->不在线
}
public List<Long> getOnlineUser(List<Long> userIds){

@ -10,6 +10,7 @@ import com.ruoyi.common.im.model.IMSendResult;
import com.ruoyi.common.im.model.IMUserInfo;
import com.ruoyi.common.im.mq.RedisMQTemplate;
import com.ruoyi.imserver.netty.UserChannelCtxMap;
import com.ruoyi.imserver.util.LongToStringConverter;
import io.netty.channel.ChannelHandlerContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -37,7 +38,7 @@ public class GroupMessageProcessor extends AbstractMessageProcessor<IMRecvInfo>
// 推送消息到用户
IMSendInfo<Object> sendInfo = new IMSendInfo<>();
sendInfo.setCmd(IMCmdType.GROUP_MESSAGE.code());
sendInfo.setData(recvInfo.getData());
sendInfo.setData(LongToStringConverter.convertLongToString(recvInfo.getData()));
channelCtx.channel().writeAndFlush(sendInfo);
// 消息发送成功确认
sendResult(recvInfo, receiver, IMSendCode.SUCCESS);
@ -62,6 +63,7 @@ public class GroupMessageProcessor extends AbstractMessageProcessor<IMRecvInfo>
result.setSender(recvInfo.getSender());
result.setReceiver(receiver);
result.setCode(sendCode.code());
// result.setData(LongToStringConverter.convertLongToString(recvInfo.getData()));
result.setData(recvInfo.getData());
// 推送到结果队列
String key = StrUtil.join(":", IMRedisKey.IM_RESULT_GROUP_QUEUE,recvInfo.getServiceName());

@ -11,6 +11,7 @@ import com.ruoyi.common.im.model.IMSendResult;
import com.ruoyi.common.im.model.IMUserInfo;
import com.ruoyi.common.im.mq.RedisMQTemplate;
import com.ruoyi.imserver.netty.UserChannelCtxMap;
import com.ruoyi.imserver.util.LongToStringConverter;
import io.netty.channel.ChannelHandlerContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -36,7 +37,7 @@ public class PrivateMessageProcessor extends AbstractMessageProcessor<IMRecvInfo
// 推送消息到用户
IMSendInfo<Object> sendInfo = new IMSendInfo<>();
sendInfo.setCmd(IMCmdType.PRIVATE_MESSAGE.code());
sendInfo.setData(recvInfo.getData());
sendInfo.setData(LongToStringConverter.convertLongToString(recvInfo.getData()));
channelCtx.channel().writeAndFlush(sendInfo);
// 消息发送成功确认
sendResult(recvInfo, IMSendCode.SUCCESS);
@ -59,10 +60,13 @@ public class PrivateMessageProcessor extends AbstractMessageProcessor<IMRecvInfo
result.setSender(recvInfo.getSender());
result.setReceiver(recvInfo.getReceivers().get(0));
result.setCode(sendCode.code());
// result.setData(LongToStringConverter.convertLongToString(recvInfo.getData()));
result.setData(recvInfo.getData());
// 推送到结果队列
String key = StrUtil.join(":", IMRedisKey.IM_RESULT_PRIVATE_QUEUE,recvInfo.getServiceName());
redisMQTemplate.opsForList().rightPush(key, result);
}
}
}

@ -0,0 +1,45 @@
package com.ruoyi.imserver.util;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import java.util.Map;
public class LongToStringConverter {
private static final ObjectMapper MAPPER = new ObjectMapper();
public static Map<String, Object> convertLongToString(Object data) {
// 利用 Jackson 的树模型,将所有数字节点转为字符串
JsonNode node = MAPPER.valueToTree(data);
convertNumberNodesToString(node);
return MAPPER.convertValue(node, new TypeReference<Map<String, Object>>() {});
}
private static void convertNumberNodesToString(JsonNode node) {
if (node.isObject()) {
ObjectNode obj = (ObjectNode) node;
obj.fieldNames().forEachRemaining(fieldName -> {
JsonNode child = obj.get(fieldName);
if (child.isNumber()) {
obj.put(fieldName, child.asText()); // 数字转字符串
} else if (child.isObject() || child.isArray()) {
convertNumberNodesToString(child);
}
});
} else if (node.isArray()) {
ArrayNode array = (ArrayNode) node;
for (int i = 0; i < array.size(); i++) {
JsonNode element = array.get(i);
if (element.isNumber()) {
array.set(i, TextNode.valueOf(element.asText())); // 数组内数字元素 → 字符串
} else if (element.isObject() || element.isArray()) {
convertNumberNodesToString(element);
}
}
}
}
}

@ -6,7 +6,7 @@ spring:
druid:
# 主库数据源
master:
url: jdbc:mysql://127.0.0.1:32769/yoga?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://127.0.0.1:32768/yoga?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# url: jdbc:mysql://127.0.0.1:3306/yj?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: '!Runpeng888'

@ -15,6 +15,6 @@ public class QuestionAnswerSheet {
private String sessionId;
private LocalDateTime submitTime;
private String ipAddress;
private String resultType;
private String resultText;
private String resultType;//结果类型pinghe, qixu, yangxu, yinxu, tanshi, shire, xueyu, qiyu, tebing, multiple
private String resultText;//结果描述文本
}

@ -10,5 +10,5 @@ public class QuestionConstitutionScore {
private Long id;
private Long sheetId;
private Long constitutionId;
private Integer totalScore;
private Integer totalScore;//总分 0-10
}

@ -9,8 +9,8 @@ public class QuestionConstitutionType {
@TableId(type = IdType.AUTO)
private Long id;
private Long surveyId;
private String name;
private String alias;
private String description;
private Integer sortOrder;
private String name;//模块名称
private String alias;//唯一标识如pinghe
private String description;//模块说明
private Integer sortOrder;//排序
}

@ -10,6 +10,6 @@ public class QuestionQuestion {
private Long id;
private Long constitutionId;
private Integer serialNumber;
private String content;
private Integer fullScore;
private String content;//题目内容
private Integer fullScore;//满分
}

@ -304,7 +304,8 @@ public class QuestionnaireServiceImpl extends ServiceImpl<QuestionAnswerSheetMap
// 规则2: 某偏颇体质总分 ≥ 8
Optional<String> highKey = biasedScores.entrySet().stream()
.filter(e -> e.getValue() >= 8) .map(Map.Entry::getKey).findFirst();
.filter(e -> e.getValue() >= 8) .max(Map.Entry.comparingByValue()).map(Map.Entry::getKey);
if (highKey.isPresent()) {
String alias = highKey.get();
String name = constitutionMap.values().stream()
@ -312,16 +313,25 @@ public class QuestionnaireServiceImpl extends ServiceImpl<QuestionAnswerSheetMap
String text = String.format("亲爱的伽人您好,您的体质为典型%s。请联系您的馆主或教练领取训练及生活方案快速调整哦。", name);
return buildResult(alias, text, scores, constitutionMap);
}
// 规则4: 两种及以上偏颇体质分数 ≥ 7
long count = biasedScores.values().stream().filter(v -> v >= 7).count();
if (count >= 2) {
String text = "亲爱的伽人您好,您的体质为复合型体质典型,建议综合调理。请联系您的馆主或教练,领取训练及生活方案,快速调整哦。";
List<String> keys = biasedScores.entrySet().stream()
.filter(e -> e.getValue() >= 7) .map(Map.Entry::getKey).collect(Collectors.toList());
// long count = biasedScores.values().stream().filter(v -> v >= 7).count();
if (keys.size() >= 1) {
String nameStr=constitutionMap.values().stream()
.filter(ct -> keys.contains(ct.getAlias()))
.map(QuestionConstitutionType::getName)
.collect(Collectors.joining(","));
String isGrOne=keys.size()==1?nameStr:String.format("复合型体质(%s)",nameStr);
String text = String.format("亲爱的伽人您好,您的体质为%s建议综合调理。请联系您的馆主或教练领取训练及生活方案快速调整哦。", isGrOne);
return buildResult("multiple", text, scores, constitutionMap);
}
// 规则3: 某偏颇体质总分在 6~7 之间
Optional<String> midKey = biasedScores.entrySet().stream()
.filter(e -> e.getValue() >= 6 && e.getValue() <= 7) .map(Map.Entry::getKey).findFirst();
if (midKey.isPresent()) {
String alias = midKey.get();
String name = constitutionMap.values().stream()
@ -330,8 +340,6 @@ public class QuestionnaireServiceImpl extends ServiceImpl<QuestionAnswerSheetMap
return buildResult(alias + "_tendency", text, scores, constitutionMap);
}
// 默认兜底(一般不会触发)
return buildResult("unknown", "无法判定体质类型,请重新测评。", scores, constitutionMap);
}

@ -33,7 +33,7 @@ public class RespBook {
private Long studentId;
private String studentName;
private Long appUserId;
// 性别0->未知1->女2->男
private String sex;
private String phone;

@ -13,6 +13,7 @@ public class RespStuBook {
private Long studentId;
private Long bookId;
private Long appUserId;
private String studentName;

@ -64,6 +64,7 @@ public class ClaTimeBookItem {
//老师信息
private Long teacherId;
private Long teacherAppId;
private String teacherName;
//*0未签到 1签到

@ -20,6 +20,7 @@ public class StudentOrderItem {
private Integer sex;
private String phone;
private String courseName;
private String cardNo;
//收费模式 hour:课时 date:时间 cycle:期
private String chargeType;
//开始时间
@ -28,6 +29,13 @@ public class StudentOrderItem {
private String expireDate;
//收款金额
private BigDecimal receiptFee;
private BigDecimal totalFee;//储值
private BigDecimal remainingTotalFee;//储值
private BigDecimal count;//次数
private BigDecimal remainingCount;//次数
private int days;//天数

@ -270,6 +270,7 @@ public class ScClaTimeServiceImpl extends ServiceImpl<ScClaTimeMapper, ScClaTime
return new RestResponse().setSuccess(false).setMessage("课程编号有误,无法预约");
}
if (claTim.getBookAttendCnt().equals(claTim.getAtClassCnt())){
return new RestResponse().setSuccess(false).setMessage("课程预约已满,无法预约");
}
@ -405,31 +406,31 @@ public class ScClaTimeServiceImpl extends ServiceImpl<ScClaTimeMapper, ScClaTime
}
}else {
//预约表
ScBookCourse book=new ScBookCourse();
book.setBookStatus(0);
book.setCourseTimeId(courseTimeId);
book.setCreateTime(new Date());
book.setStudentId(studentId);
book.setCardNo(selected.getCardNo());
book.setChargeType(selected.getChargeType());
book.setDeductCnt(selected.getDeductCnt());
book.setDeductFee(selected.getDeductFee());
book.setTenantId(claTim.getTenantId());
book.setCourseId(claTim.getCourseId());
book.setDepartId(claTim.getStoreId());
bookCourse=new ScBookCourse();
bookCourse.setBookStatus(0);
bookCourse.setCourseTimeId(courseTimeId);
bookCourse.setCreateTime(new Date());
bookCourse.setStudentId(studentId);
bookCourse.setCardNo(selected.getCardNo());
bookCourse.setChargeType(selected.getChargeType());
bookCourse.setDeductCnt(selected.getDeductCnt());
bookCourse.setDeductFee(selected.getDeductFee());
bookCourse.setTenantId(claTim.getTenantId());
bookCourse.setCourseId(claTim.getCourseId());
bookCourse.setDepartId(claTim.getStoreId());
//生成一个code 用于消课
Long code = IDGenerator.generateId();
bookCourseMapper.insert(book);
bookCourseMapper.insert(bookCourse);
try {
byte[] qrCode= CodeUtil.generateQrCodeBytes(book.getId().toString());
book.setQrCode(Base64.encode(qrCode));
byte[] qrCode= CodeUtil.generateQrCodeBytes(bookCourse.getId().toString());
bookCourse.setQrCode(Base64.encode(qrCode));
}catch (Exception e){
}
book.setCode(book.getId());
bookCourseMapper.updateById(book);
bookCourse.setCode(bookCourse.getId());
bookCourseMapper.updateById(bookCourse);
//预约记录
addBookLogFromApp(0,book.getId());
addBookLogFromApp(0,bookCourse.getId());
//预约成功 后 首次预约激活
Map<String, List<ScMemberCard>> cardMap =cardList.stream().collect(Collectors.groupingBy(ScMemberCard::getCardNo));
@ -440,6 +441,7 @@ public class ScClaTimeServiceImpl extends ServiceImpl<ScClaTimeMapper, ScClaTime
edtiCard.setActivationDate(LocalDate.now());
edtiCard.setStatus("ACTIVE");
edtiCard.setId(card.getId());
memberCardsMapper.updateById(edtiCard);
}
}
@ -447,9 +449,26 @@ public class ScClaTimeServiceImpl extends ServiceImpl<ScClaTimeMapper, ScClaTime
//本节课
claTim.setBookAttendCnt(claTim.getBookAttendCnt()+1);
claTimeMapper.updateById(claTim);
jpush(bookCourse.getId(),
selected.getCourseName(),
SecurityUtils.getAppLoginUser().getAppUser().getId());
return new RestResponse().setSuccess(true).setMessage("预约成功");
}
public void jpush(Long bookId,String coursesName,Long recvId){
AppUser recvUser= appUserService.getById(recvId);
if (StrUtil.isNotEmpty(recvUser.getRegisterId())){
String msg = String.format(
"%s%s",
coursesName, "课程预约成功!"
);
Map<String, String> extras=new HashMap<>();
extras.put("type","bookCourses");//type推送类型bookCourses 预约课程 groupMessage群聊消息 privateMessage私信消息
extras.put("pagePath","/pages/book/book");//pagePath跳转页面
extras.put("bookId",bookId.toString());//bookId预约id
JpushClientUtil.pushByRegistrationId(Arrays.asList(recvUser.getRegisterId()),"瑜伽汇",msg,extras);
}
}
//约课次数
public Boolean checkBookNum(Long cardTypeId,String cardNo){
RespMemberTypeCourses type= memberCardsMapper.getMemberTypeById(cardTypeId);
@ -660,25 +679,25 @@ public class ScClaTimeServiceImpl extends ServiceImpl<ScClaTimeMapper, ScClaTime
updateBook.setBookStatus(5);
f=bookCourseMapper.updateById(updateBook);
addBookLogFromApp(5,bookId);
return new RestResponse().setSuccess(true).setMessage("成功!");
}
if (ArrayUtil.containsAny(roleId, 104L)&& dbBookStatus==0){
updateBook.setBookStatus(1);
f=bookCourseMapper.updateById(updateBook);
addBookLogFromApp(1,bookId);
return new RestResponse().setSuccess(true).setMessage("成功!");
}
if (ArrayUtil.containsAny(roleId, 105L)&& dbBookStatus==1){
updateBook.setBookStatus(2);
f=bookCourseMapper.updateById(updateBook);
addBookLogFromApp(2,bookId);
return new RestResponse().setSuccess(true).setMessage("成功!");
}
if (f>0){
return new RestResponse().setSuccess(true).setMessage("成功!");
}else {
return new RestResponse().setSuccess(false).setMessage("请刷新预约状态!");
}
}

@ -2,6 +2,8 @@ package com.ruoyi.im.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import java.util.Date;
@ -23,16 +25,19 @@ public class GroupMessage {
* id
*/
@TableId
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
/**
* id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long groupId;
/**
* id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long sendId;
/**

@ -1,6 +1,8 @@
package com.ruoyi.im.domain;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import java.util.Date;
@ -20,16 +22,19 @@ public class PrivateMessage {
/**
* id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
/**
* id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long sendId;
/**
* id
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long recvId;
/**

@ -27,7 +27,7 @@ public class GroupMessageDTO {
private Integer type;
@Schema(description = "是否回执消息")
private Boolean receipt = false;
private Boolean receipt = true;
@Size(max = 20, message = "一次最多只能@20个小伙伴哦")
@Schema(description = "被@用户列表")

@ -1,5 +1,6 @@
package com.ruoyi.im.domain.vo;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@ -23,4 +24,9 @@ public class FriendVO {
@Schema(description = "是否已删除")
private Boolean deleted;
private Long roleId;
private Long storeId;
}

@ -1,6 +1,7 @@
package com.ruoyi.im.domain.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ruoyi.common.serializer.DateToLongSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@ -12,14 +13,20 @@ import java.util.List;
public class GroupMessageVO {
@Schema(description = "消息id")
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
@Schema(description = "群聊id")
@JsonSerialize(using = ToStringSerializer.class)
private Long groupId;
@Schema(description = " 发送者id")
@JsonSerialize(using = ToStringSerializer.class)
private Long sendId;
@Schema(description = " 已读者id")
private Long readedId;
@Schema(description = " 发送者昵称")
private String sendNickName;

@ -0,0 +1,19 @@
package com.ruoyi.im.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
@Data
@AllArgsConstructor
public class OnlineTerminalVO {
@Schema(description = "用户id")
private Long userId;
@Schema(description = "在线终端类型")
private List<Integer> terminals;
}

@ -2,6 +2,7 @@ package com.ruoyi.im.domain.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.ruoyi.common.serializer.DateToLongSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@ -13,12 +14,15 @@ import java.util.Date;
public class PrivateMessageVO {
@Schema(description = " 消息id")
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
@Schema(description = " 发送者id")
@JsonSerialize(using = ToStringSerializer.class)
private Long sendId;
@Schema(description = " 接收者id")
@JsonSerialize(using = ToStringSerializer.class)
private Long recvId;
@Schema(description = " 发送内容")

@ -2,12 +2,13 @@ package com.ruoyi.im.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.im.domain.Friend;
import com.ruoyi.im.domain.vo.FriendVO;
import java.util.List;
public interface FriendMapper extends BaseMapper<Friend> {
List<Friend> getFriendListForConsumer(Long storeId);
List<Friend> getFriendListForManage(Long userId);
List<FriendVO> getFriendListForConsumer(Long storeId);
List<FriendVO> getFriendListForManage(Long userId);
}

@ -3,6 +3,7 @@ package com.ruoyi.im.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.im.domain.Friend;
import com.ruoyi.im.domain.vo.FriendVO;
import com.ruoyi.im.domain.vo.OnlineTerminalVO;
import com.sun.javafx.collections.MappingChange;
import java.util.List;
@ -67,5 +68,11 @@ public interface FriendService extends IService<Friend> {
*/
void bindFriend(Long userId, Long friendId);
/**
* 线
*
* @param userIds id,
* @return 线
*/
List<OnlineTerminalVO> getOnlineTerminals(String userIds);
}

@ -3,6 +3,7 @@ package com.ruoyi.im.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.im.domain.Group;
import com.ruoyi.im.domain.dto.GroupInviteDTO;
import com.ruoyi.im.domain.vo.GroupMemberVO;
import com.ruoyi.im.domain.vo.GroupVO;
import java.util.List;
@ -42,5 +43,11 @@ public interface GroupService extends IService<Group> {
*/
Group getAndCheckById(Long groupId);
/**
*
*
* @param groupId id
* @return List<GroupMemberVO>
**/
List<GroupMemberVO> findGroupMembers(Long groupId);
}

@ -11,11 +11,14 @@ import com.ruoyi.basic.service.YjAppUserService;
import com.ruoyi.common.core.domain.entity.AppUser;
import com.ruoyi.common.im.constant.RedisKey;
import com.ruoyi.common.exception.GlobalException;
import com.ruoyi.common.im.enums.IMTerminalType;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.im.domain.Friend;
import com.ruoyi.im.domain.vo.FriendVO;
import com.ruoyi.im.domain.vo.OnlineTerminalVO;
import com.ruoyi.im.service.FriendService;
import com.ruoyi.im.mapper.FriendMapper;
import com.ruoyi.imclient.IMClient;
import com.ruoyi.system.service.SysTeacherService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -26,10 +29,7 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@ -38,29 +38,31 @@ import java.util.stream.Collectors;
@CacheConfig(cacheNames = RedisKey.IM_CACHE_FRIEND)
public class FriendServiceImpl extends ServiceImpl<FriendMapper, Friend> implements FriendService {
private final IMClient imClient;
private final YjAppUserService yjAppUserService;
private final SysTeacherService t;
@Override
public Map findAllFriends() {
Long userId= SecurityUtils.getAppLoginUser().getAppUser().getId();
Map<String, List<Friend>> map=new HashMap<>();
Map<String, List<FriendVO>> map=new HashMap<>();
Long manageId= SecurityUtils.getAppLoginUser().getAppUser().getManageAccountId();
if (ObjectUtil.isNotEmpty(manageId)){
List<Friend> list=baseMapper.getFriendListForManage(userId);
List<FriendVO> list=baseMapper.getFriendListForManage(userId);
map.put("consumer",list);
map.put("all",list);//所有好友
return map;
}
Long storeId=SecurityUtils.getAppLoginUser().getAppUser().getVisitStore();
List<Friend> list=baseMapper.getFriendListForConsumer(storeId);
Map<Long, List<Friend>> friendMap = list.stream().collect(Collectors.groupingBy(Friend::getRoleId));
List<FriendVO> list=baseMapper.getFriendListForConsumer(storeId);
Map<Long, List<FriendVO>> friendMap = list.stream().collect(Collectors.groupingBy(FriendVO::getRoleId));
map.put("storeManager",friendMap.get(105l));//店长
map.put("coach",friendMap.get(104l));//教练
map.put("adviser",friendMap.get(103l));//课程顾问
map.put("customerService",friendMap.get(108l));//客服
map.put("all",list);//所有好友
return map;
@ -268,4 +270,17 @@ public class FriendServiceImpl extends ServiceImpl<FriendMapper, Friend> impleme
// imClient.sendPrivateMessage(sendMessage);
}
@Override
public List<OnlineTerminalVO> getOnlineTerminals(String userIds) {
List<Long> userIdList = Arrays.stream(userIds.split(",")).map(Long::parseLong).collect(Collectors.toList());
// 查询在线的终端
Map<Long, List<IMTerminalType>> terminalMap = imClient.getOnlineTerminal(userIdList);
// 组装vo
List<OnlineTerminalVO> vos = new LinkedList<>();
terminalMap.forEach((userId, types) -> {
List<Integer> terminals = types.stream().map(IMTerminalType::code).collect(Collectors.toList());
vos.add(new OnlineTerminalVO(userId, terminals));
});
return vos;
}
}

@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.service.YjAppUserService;
import com.ruoyi.common.core.domain.entity.AppUser;
import com.ruoyi.common.exception.GlobalException;
import com.ruoyi.common.im.constant.IMConstant;
@ -21,6 +22,7 @@ import com.ruoyi.common.im.model.IMUserInfo;
import com.ruoyi.common.im.util.BeanUtils;
import com.ruoyi.common.im.util.CommaTextUtils;
import com.ruoyi.common.im.util.ThreadPoolExecutorFactory;
import com.ruoyi.common.utils.JpushClientUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.im.domain.Group;
import com.ruoyi.im.domain.GroupMember;
@ -52,6 +54,7 @@ import java.util.stream.Collectors;
public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, GroupMessage>
implements GroupMessageService {
private final GroupService groupService;
private final YjAppUserService appUserService;
private final GroupMemberService groupMemberService;
private final RedisTemplate<String, Object> redisTemplate;
private final IMClient imClient;
@ -96,25 +99,41 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
sendMessage.setSendResult(false);
sendMessage.setData(msgInfo);
imClient.sendGroupMessage(sendMessage);
jpush(group.getId(),group.getName(),msg.getContent(),userIds);
log.info("发送群聊消息发送id:{},群聊id:{},内容:{}", session.getId(), dto.getGroupId(), dto.getContent());
return msgInfo;
}
public void jpush(Long groupId,String groupName,String contentd,List<Long> recvIds){
List<AppUser> recvUsers= appUserService.listByIds(recvIds);
List<String> registerIds=recvUsers.stream().filter(user->StrUtil.isNotBlank(user.getRegisterId())).map(it -> it.getRegisterId()).collect(Collectors.toList());
if (registerIds.size()>0){
String msg = String.format(
"%s%s",
groupName, contentd
);
Map<String, String> extras=new HashMap<>();
extras.put("type","groupMessage");
extras.put("groupId",groupId.toString());
extras.put("pagePath","/pages/chatbox/chat-box");
JpushClientUtil.pushByRegistrationId(registerIds,"瑜伽汇",msg,extras);
}
}
@Transactional
@Override
public GroupMessageVO recallMessage(Long id) {
AppUser session = SecurityUtils.getAppLoginUser().getAppUser();
GroupMessage msg = this.getById(id);
if (Objects.isNull(msg)) {
throw new GlobalException("消息不存在");
}
if (!msg.getSendId().equals(SecurityUtils.getAppLoginUser().getAppUserId())) {
if (!msg.getSendId().equals(session.getId())) {
throw new GlobalException("这条消息不是由您发送,无法撤回");
}
if (System.currentTimeMillis() - msg.getSendTime().getTime() > IMConstant.ALLOW_RECALL_SECOND * 1000) {
throw new GlobalException("消息已发送超过5分钟无法撤回");
}
// 判断是否在群里
GroupMember member = groupMemberService.findByGroupAndUserId(msg.getGroupId(), SecurityUtils.getAppLoginUser().getAppUserId());
GroupMember member = groupMemberService.findByGroupAndUserId(msg.getGroupId(), session.getId());
if (Objects.isNull(member) || Boolean.TRUE.equals(member.getQuit())) {
throw new GlobalException("您已不在群聊里面,无法撤回消息");
}
@ -126,7 +145,7 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
recallMsg.setStatus(MessageStatus.UNSEND.code());
recallMsg.setType(MessageType.RECALL.code());
recallMsg.setGroupId(msg.getGroupId());
recallMsg.setSendId(SecurityUtils.getAppLoginUser().getAppUserId());
recallMsg.setSendId(session.getId());
recallMsg.setSendNickName(member.getShowNickName());
recallMsg.setContent(id.toString());
recallMsg.setSendTime(new Date());
@ -135,11 +154,11 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
List<Long> userIds = groupMemberService.findUserIdsByGroupId(msg.getGroupId());
GroupMessageVO msgInfo = BeanUtils.copyProperties(recallMsg, GroupMessageVO.class);
IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
sendMessage.setSender(new IMUserInfo(SecurityUtils.getAppLoginUser().getAppUserId(), 1));
sendMessage.setSender(new IMUserInfo(session.getId(), 1));
sendMessage.setRecvIds(userIds);
sendMessage.setData(msgInfo);
imClient.sendGroupMessage(sendMessage);
log.info("撤回群聊消息发送id:{},群聊id:{},内容:{}", SecurityUtils.getAppLoginUser().getAppUserId(), msg.getGroupId(), msg.getContent());
log.info("撤回群聊消息发送id:{},群聊id:{},内容:{}", session.getId(), msg.getGroupId(), msg.getContent());
return msgInfo;
}
@ -159,6 +178,7 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
return;
}
// 只能拉取最近3个月的,移动端只拉最近一个月
int months = new Integer(1).equals(IMTerminalType.APP.code()) ? 1 : 3;
Date minDate = DateUtils.addMonths(new Date(), -months);
@ -188,6 +208,7 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
// 推送消息
AtomicInteger sendCount = new AtomicInteger();
messageGroupMap.forEach((groupId, groupMessages) -> {
// 第一次拉取时,一个群最多推送1w条消息防止前端接收能力溢出导致卡顿
List<GroupMessage> sendMessages = groupMessages;
if (minId <= 0 && groupMessages.size() > 10000) {
@ -198,24 +219,32 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
Object o = redisTemplate.opsForHash().get(key,session.getId().toString());
long readedMaxId = Objects.isNull(o) ? -1 : Long.parseLong(o.toString());
Map<Object, Object> maxIdMap = null;
for (GroupMessage m : sendMessages) {
// 排除加群之前的消息
GroupMember member = groupMemberMap.get(m.getGroupId());
if (DateUtil.compare(member.getCreatedTime(), m.getSendTime()) > 0) {
continue;
}
// 排除不需要接收的消息
List<String> recvIds = CommaTextUtils.asList(m.getRecvIds());
if (!recvIds.isEmpty() && !recvIds.contains(session.getId().toString())) {
continue;
}
// 组装vo
GroupMessageVO vo = BeanUtils.copyProperties(m, GroupMessageVO.class);
// 被@用户列表
List<String> atIds = CommaTextUtils.asList(m.getAtUserIds());
vo.setAtUserIds(atIds.stream().map(Long::parseLong).collect(Collectors.toList()));
// 填充状态
vo.setStatus(readedMaxId >= m.getId() ? MessageStatus.READED.code() : MessageStatus.UNSEND.code());
// 针对回执消息填充已读人数
if (m.getReceipt()) {
if (Objects.isNull(maxIdMap)) {
@ -224,6 +253,7 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
int count = getReadedUserIds(maxIdMap, m.getId(), m.getSendId()).size();
vo.setReadedCount(count);
}
// 推送
IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
sendMessage.setSender(new IMUserInfo(m.getSendId(), IMTerminalType.WEB.code()));
@ -235,15 +265,17 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
imClient.sendGroupMessage(sendMessage);
sendCount.getAndIncrement();
}
System.out.println("推送结束123");
});
// 关闭加载中标志
this.sendLoadingMessage(false,session);
log.info("拉取离线群聊消息,用户id:{},数量:{}",SecurityUtils.getAppLoginUser().getAppUserId(), sendCount.get());
log.info("拉取离线群聊消息,用户id:{},数量:{}",session.getId(), sendCount.get());
});
}
@Override
public void readedMessage(Long groupId) {
AppUser session = SecurityUtils.getAppLoginUser().getAppUser();
// 取出最后的消息id
LambdaQueryWrapper<GroupMessage> wrapper = Wrappers.lambdaQuery();
wrapper.eq(GroupMessage::getGroupId, groupId).orderByDesc(GroupMessage::getId).last("limit 1")
@ -256,10 +288,10 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
GroupMessageVO msgInfo = new GroupMessageVO();
msgInfo.setType(MessageType.READED.code());
msgInfo.setSendTime(new Date());
msgInfo.setSendId(SecurityUtils.getAppLoginUser().getAppUserId());
msgInfo.setSendId(session.getId());
msgInfo.setGroupId(groupId);
IMGroupMessage<GroupMessageVO> sendMessage = new IMGroupMessage<>();
sendMessage.setSender(new IMUserInfo(SecurityUtils.getAppLoginUser().getAppUserId(), 1));
sendMessage.setSender(new IMUserInfo(session.getId(), 1));
sendMessage.setSendToSelf(true);
sendMessage.setData(msgInfo);
sendMessage.setSendResult(true);
@ -267,9 +299,9 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
// 已读消息key
String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId);
// 原来的已读消息位置
Object maxReadedId = redisTemplate.opsForHash().get(key,SecurityUtils.getAppLoginUser().getAppUserId().toString());
Object maxReadedId = redisTemplate.opsForHash().get(key,session.getId().toString());
// 记录已读消息位置
redisTemplate.opsForHash().put(key, SecurityUtils.getAppLoginUser().getAppUserId().toString(), message.getId());
redisTemplate.opsForHash().put(key, session.getId().toString(), message.getId());
// 推送消息回执,刷新已读人数显示
wrapper = Wrappers.lambdaQuery();
wrapper.eq(GroupMessage::getGroupId, groupId);
@ -293,10 +325,11 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
msgInfo.setId(receiptMessage.getId());
msgInfo.setGroupId(groupId);
msgInfo.setReadedCount(readedCount);
msgInfo.setReadedId(session.getId());
msgInfo.setReceiptOk(receiptMessage.getReceiptOk());
msgInfo.setType(MessageType.RECEIPT.code());
sendMessage = new IMGroupMessage<>();
sendMessage.setSender(new IMUserInfo(SecurityUtils.getAppLoginUser().getAppUserId(), 1));
sendMessage.setSender(new IMUserInfo(session.getId(), 1));
sendMessage.setRecvIds(userIds);
sendMessage.setData(msgInfo);
sendMessage.setSendToSelf(false);
@ -308,12 +341,13 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
@Override
public List<Long> findReadedUsers(Long groupId, Long messageId) {
AppUser session = SecurityUtils.getAppLoginUser().getAppUser();
GroupMessage message = this.getById(messageId);
if (Objects.isNull(message)) {
throw new GlobalException("消息不存在");
}
// 是否在群聊里面
GroupMember member = groupMemberService.findByGroupAndUserId(groupId, SecurityUtils.getAppLoginUser().getAppUserId());
GroupMember member = groupMemberService.findByGroupAndUserId(groupId, session.getId());
if (Objects.isNull(member) || member.getQuit()) {
throw new GlobalException("您已不在群聊里面");
}
@ -327,9 +361,10 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
@Override
public List<GroupMessageVO> findHistoryMessage(Long groupId, Long page, Long size) {
AppUser session = SecurityUtils.getAppLoginUser().getAppUser();
page = page > 0 ? page : 1;
size = size > 0 ? size : 10;
Long userId = SecurityUtils.getAppLoginUser().getAppUserId();
Long userId = session.getId();
long stIdx = (page - 1) * size;
// 群聊成员信息
GroupMember member = groupMemberService.findByGroupAndUserId(groupId, userId);
@ -373,15 +408,17 @@ public class GroupMessageServiceImpl extends ServiceImpl<GroupMessageMapper, Gro
sendMessage.setData(msgInfo);
sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false);
System.out.println("发送LOADING"+isLoadding);
imClient.sendGroupMessage(sendMessage);
}
@Override
public String getMaxReadedId(Long groupId) {
AppUser session = SecurityUtils.getAppLoginUser().getAppUser();
// 已读消息key
String key = StrUtil.join(":", RedisKey.IM_GROUP_READED_POSITION, groupId);
// 原来的已读消息位置
Object maxReadedId = redisTemplate.opsForHash().get(key,SecurityUtils.getAppLoginUser().getAppUserId().toString());
Object maxReadedId = redisTemplate.opsForHash().get(key,session.getId().toString());
if (ObjectUtil.isEmpty(maxReadedId)) {
return "-1";

@ -23,6 +23,7 @@ import com.ruoyi.im.domain.Group;
import com.ruoyi.im.domain.GroupMember;
import com.ruoyi.im.domain.GroupMessage;
import com.ruoyi.im.domain.dto.GroupInviteDTO;
import com.ruoyi.im.domain.vo.GroupMemberVO;
import com.ruoyi.im.domain.vo.GroupMessageVO;
import com.ruoyi.im.domain.vo.GroupVO;
import com.ruoyi.im.mapper.GroupMapper;
@ -200,12 +201,16 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
}
private GroupVO convert(Group group, GroupMember member) {
List<Long> members = groupMemberService.findUserIdsByGroupId(group.getId());
List<Long> OtherMembers=members.stream().filter(it->!it.equals(member.getUserId())).collect(Collectors.toList());
GroupVO vo = BeanUtils.copyProperties(group, GroupVO.class);
vo.setRemarkGroupName(member.getRemarkGroupName());
vo.setRemarkNickName(member.getRemarkNickName());
vo.setShowNickName(member.getShowNickName());
vo.setShowGroupName(StrUtil.blankToDefault(member.getRemarkGroupName(), group.getName()));
vo.setQuit(member.getQuit());
vo.setCustomerService(OtherMembers.get(0));//客服id
vo.setInstructor(OtherMembers.get(1));//教练id
return vo;
}
@ -224,5 +229,19 @@ public class GroupServiceImpl extends ServiceImpl<GroupMapper, Group> implements
sendMessage.setSendToSelf(sendToSelf);
imClient.sendGroupMessage(sendMessage);
}
@Override
public List<GroupMemberVO> findGroupMembers(Long groupId) {
Group group = getAndCheckById(groupId);
List<GroupMember> members = groupMemberService.findByGroupId(groupId);
List<Long> userIds = members.stream().map(GroupMember::getUserId).collect(Collectors.toList());
List<Long> onlineUserIds = imClient.getOnlineUser(userIds);
return members.stream().map(m -> {
GroupMemberVO vo = BeanUtils.copyProperties(m, GroupMemberVO.class);
vo.setShowNickName(m.getShowNickName());
vo.setShowGroupName(StrUtil.blankToDefault(m.getRemarkGroupName(), group.getName()));
vo.setOnline(onlineUserIds.contains(m.getUserId()));
return vo;
}).sorted((m1, m2) -> m2.getOnline().compareTo(m1.getOnline())).collect(Collectors.toList());
}
}

@ -1,11 +1,15 @@
package com.ruoyi.im.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException;
import com.ruoyi.basic.service.YjAppUserService;
import com.ruoyi.common.core.domain.entity.AppUser;
import com.ruoyi.common.im.constant.IMConstant;
import com.ruoyi.common.im.enums.IMTerminalType;
@ -15,6 +19,7 @@ import com.ruoyi.common.im.enums.MessageStatus;
import com.ruoyi.common.im.enums.MessageType;
import com.ruoyi.common.exception.GlobalException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.JpushClientUtil;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.im.util.BeanUtils;
import com.ruoyi.common.im.util.ThreadPoolExecutorFactory;
@ -42,6 +47,7 @@ import com.ruoyi.mall.service.impl.OrderService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DeadlockLoserDataAccessException;
import org.springframework.data.domain.PageImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -57,22 +63,23 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
implements PrivateMessageService {
private final FriendService friendService;
private final YjAppUserService appUserService;
private final IMClient imClient;
private final SensitiveFilterUtil sensitiveFilterUtil;
private static final ScheduledThreadPoolExecutor EXECUTOR = ThreadPoolExecutorFactory.getThreadPoolExecutor();
@Override
public PrivateMessageVO sendMessage(PrivateMessageDTO dto) {
Long userId= SecurityUtils.getAppLoginUser().getAppUser().getId();
AppUser appUser= SecurityUtils.getAppLoginUser().getAppUser();
//无需验证好友
Boolean isFriends = friendService.isFriend(userId, dto.getRecvId());
Boolean isFriends = friendService.isFriend(appUser.getId(), dto.getRecvId());
if (Boolean.FALSE.equals(isFriends)) {
friendService.addFriend(dto.getRecvId());
// throw new GlobalException("您已不是对方好友,无法发送消息");
}
// 保存消息
PrivateMessage msg = BeanUtils.copyProperties(dto, PrivateMessage.class);
msg.setSendId(userId);
msg.setSendId(appUser.getId());
msg.setStatus(MessageStatus.UNSEND.code());
msg.setSendTime(new Date());
// 过滤内容中的敏感词
@ -83,16 +90,34 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
// 推送消息
PrivateMessageVO msgInfo = BeanUtils.copyProperties(msg, PrivateMessageVO.class);
IMPrivateMessage<PrivateMessageVO> sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(userId,1));
sendMessage.setSender(new IMUserInfo(appUser.getId(),1));
sendMessage.setRecvId(msgInfo.getRecvId());
sendMessage.setSendToSelf(true);
sendMessage.setData(msgInfo);
sendMessage.setSendResult(true);
imClient.sendPrivateMessage(sendMessage);
log.info("发送私聊消息发送id:{},接收id:{},内容:{}", userId, dto.getRecvId(), dto.getContent());
jpush(appUser.getId(),appUser.getNickName(),msg.getContent(),dto.getRecvId());
log.info("发送私聊消息发送id:{},接收id:{},内容:{}", appUser.getId(), dto.getRecvId(), dto.getContent());
return msgInfo;
}
public void jpush(Long sendId,String senderName,String contentd,Long recvId){
if (ObjectUtil.isNotEmpty(recvId)){
String registerId= appUserService.getById(recvId).getRegisterId();
if (StrUtil.isNotBlank(registerId)){
String msg = String.format(
"%s%s",
senderName, contentd
);
Map<String, String> extras=new HashMap<>();
extras.put("type","privateMessage");
extras.put("senderId",sendId.toString());
extras.put("pagePath","/pages/chatbox/chat-box");
JpushClientUtil.pushByRegistrationId(Arrays.asList(registerId),"瑜伽汇",msg,extras);
}
}
}
@Transactional
@Override
public PrivateMessageVO recallMessage(Long id) {
@ -188,6 +213,7 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
log.info("拉取聊天记录用户id:{},好友id:{},数量:{}", userId, friendId, messageInfos.size());
return messageInfos;
}
@Transactional(rollbackFor = Exception.class)
@Override
public void readedMessage(Long friendId) {
@ -206,11 +232,11 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
// 推送回执消息给对方,更新已读状态
msgInfo = new PrivateMessageVO();
msgInfo.setType(MessageType.RECEIPT.code());
msgInfo.setSendId(friendId);
msgInfo.setRecvId(session.getId());
msgInfo.setSendId(session.getId());
msgInfo.setRecvId(friendId);
sendMessage = new IMPrivateMessage<>();
sendMessage.setSender(new IMUserInfo(friendId, 1));
sendMessage.setRecvId(session.getId());
sendMessage.setSender(new IMUserInfo(session.getId(), 1));
sendMessage.setRecvId(friendId);
sendMessage.setSendToSelf(false);
sendMessage.setSendResult(false);
sendMessage.setData(msgInfo);
@ -230,8 +256,8 @@ public class PrivateMessageServiceImpl extends ServiceImpl<PrivateMessageMapper,
AppUser session=SecurityUtils.getAppLoginUser().getAppUser();
LambdaQueryWrapper<PrivateMessage> wrapper = Wrappers.lambdaQuery();
wrapper
.eq(PrivateMessage::getSendId, friendId)
.eq(PrivateMessage::getRecvId, session.getId())
.eq(PrivateMessage::getSendId, session.getId())
.eq(PrivateMessage::getRecvId, friendId)
.eq(PrivateMessage::getStatus, MessageStatus.READED.code())
.orderByDesc(PrivateMessage::getId)
.select(PrivateMessage::getId).last("limit 1");

@ -19,7 +19,8 @@
c.room_name,
b.teacher_name,
b.avatar_img as pic,
d.store_name
d.store_name,
(select app_user_id from yj_app_user where manage_account_id= b.user_id) as teacher_app_id
from
sc_book_course a,

@ -141,6 +141,7 @@
b.sex,
b.phone,
b.sex,
b.app_user_id,
a.id as book_id,
a.book_status,
a.check_in,
@ -183,10 +184,15 @@
year(curdate())-year(c.birth_day) as age,
c.sex,
c.phone,
b.card_no as course_name,
b.card_no ,
b.charge_type,
b.total_fee,
b.remaining_total_fee,
b.total_count as count,
b.remaining_count,
b.total_days as days,
b.activation_date as begin_date,
b.expiry_date as end_date,
b.expiry_date as expire_date,
a.receipt_fee
-- from sc_order a,sc_order_detail b,sc_student c
from sc_order a,sc_member_cards b,sc_student c
@ -252,8 +258,11 @@
c.student_id,
c.student_name,
c.sex,
c.phone
from sc_cla_time a,sc_book_course b,sc_student c
c.phone,
c.app_user_id
from sc_cla_time a,
sc_book_course b,
sc_student c
WHERE a.course_time_id=b.course_time_id and b.student_id=c.student_id
-- and b.book_status=0
and a.teacher_id=#{teacherId}
@ -277,7 +286,8 @@
c.student_id,
c.student_name,
c.sex,
c.phone
c.phone,
c.app_user_id
from sc_cla_time a,sc_book_course b,sc_student c
WHERE a.course_time_id=b.course_time_id and b.student_id=c.student_id

@ -22,12 +22,12 @@
and a.user_id=#{userId}
</select>
<select id="getFriendListForConsumer" resultType="com.ruoyi.im.domain.Friend">
<select id="getFriendListForConsumer" resultType="com.ruoyi.im.domain.vo.FriendVO">
select
d.app_user_id as friend_id,
d.app_user_id as id,
-- CONCAT(d.nick_name,'(',b.store_name,')') as friend_nick_name,
d.nick_name as friend_nick_name,
d.avatar as friend_head_image,
d.nick_name as nick_name,
d.avatar as head_image,
e.role_id,
b.dept_id as store_id
from yj_store b
@ -40,12 +40,12 @@
and b.dept_id=#{storeId}
</select>
<select id="getFriendListForManage" resultType="com.ruoyi.im.domain.Friend">
select a.id,
a.user_id,
a.friend_id,
a.friend_nick_name,
b.avatar as friend_head_image
<select id="getFriendListForManage" resultType="com.ruoyi.im.domain.vo.FriendVO">
select
b.app_user_id as id,
b.nick_name as nick_name,
b.avatar as head_image,
a.deleted
from im_friend a,
yj_app_user b
WHERE a.friend_id=b.app_user_id

Loading…
Cancel
Save