Compare commits

...

31 Commits

Author SHA1 Message Date
ed180d6f18 集成MQTT,优化代码 2025-06-27 10:30:27 +08:00
c0c33f6c2e 集成MQTT 2025-06-26 17:14:34 +08:00
953ffdfb28 app显示还回为空的参数 2025-06-25 17:28:57 +08:00
6b9a09c9e8 Merge remote-tracking branch 'origin/dyf-app' into main_app权限适配 2025-06-25 17:07:17 +08:00
64729a223d app端登录权限验证 2025-06-25 16:15:33 +08:00
ec03919c78 Merge remote-tracking branch 'origin/main_app权限适配' into dyf-app
# Conflicts:
#	fys-system/src/main/java/com/fuyuanshen/modules/security/security/app/AppTokenProvider.java
2025-06-25 08:40:02 +08:00
c61f2a7265 app登录响应状态码修改 2025-06-24 18:35:30 +08:00
ae88cc16cc 修改app用户信息 2025-06-24 17:43:45 +08:00
b467e02ff6 APP用户设备类型列表 2025-06-24 16:19:29 +08:00
d64216ee5f APP用户设备绑定 2025-06-24 14:34:44 +08:00
a7bb1d8e84 app端忘记密码redis缓存key字段优化 2025-06-24 13:58:00 +08:00
1f7f4bf537 app端忘记密码功能实现 2025-06-24 13:38:36 +08:00
9a975b36c5 APP用户设备绑定 2025-06-24 11:40:54 +08:00
401d6752cf APP/小程序用户设备绑定 2025-06-24 10:24:35 +08:00
3e0394eaea Merge branch 'main_app权限适配' into dyf-app 2025-06-24 09:16:30 +08:00
d91012ee1b app权限适配2 2025-06-24 09:14:19 +08:00
1438178f70 Merge branch 'main_app权限适配' 2025-06-23 18:53:31 +08:00
fb94eaf97c app权限适配 2025-06-23 18:34:30 +08:00
089e0df124 小程序:相关接口 2025-06-23 15:22:07 +08:00
8f810ff315 设备创建人 2025-06-23 11:07:45 +08:00
a72f606b36 Merge remote-tracking branch 'origin/main' 2025-06-23 10:53:51 +08:00
dcbe9c4dd2 设备类型导出 2025-06-23 10:53:29 +08:00
3278b789dd 还原提交 2025-06-23 10:19:28 +08:00
735c12951c 1111222 2025-06-23 10:17:47 +08:00
568ad0ebf4 1111 2025-06-23 10:16:26 +08:00
a13df9bfa6 设备导出 2025-06-21 18:02:25 +08:00
e5c6cc664d 查询设备 2025-06-21 17:16:33 +08:00
f9a340c77c 查询设备 2025-06-21 13:48:37 +08:00
a02819261d 分配设备 2025-06-20 19:20:56 +08:00
b11d3ccd61 分配设备 2025-06-20 18:42:20 +08:00
7549219213 登录 2025-06-20 17:32:58 +08:00
78 changed files with 2060 additions and 384 deletions

View File

@ -17,6 +17,7 @@ package com.fuyuanshen.config;
import com.fuyuanshen.utils.SecurityUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;

View File

@ -0,0 +1,8 @@
package com.fuyuanshen.constants;
public class RedisConstants {
public static final String APP_FORGOT_PASSWORD_SMS_TOKEN = "app_sms_forgotPassword:";
public static final String APP_REGISTER_SMS_TOKEN = "app_sms_register:";
}

View File

@ -16,6 +16,7 @@
package com.fuyuanshen.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.jwt.Claims;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.alibaba.fastjson2.JSON;

View File

@ -0,0 +1,32 @@
package com.fuyuanshen.utils.enums;
/**
* @author: 默苍璃
* @date: 2025-06-2019:08
*/
/**
* 表示 NanoId 生成时使用的长度配置
*/
public enum NanoIdLengthEnum {
/**
* 默认设备类型 ID 长度
*/
DEVICE_TYPE_ID(10),
/**
* 更长的唯一标识符,用于高并发场景
*/
HIGH_CONCURRENCY(15);
private final int length;
NanoIdLengthEnum(int length) {
this.length = length;
}
public int getLength() {
return length;
}
}

View File

@ -0,0 +1,93 @@
package com.fuyuanshen.modules.MiniProgram.controller;
import com.fuyuanshen.annotation.Log;
import com.fuyuanshen.annotation.rest.AnonymousPostMapping;
import com.fuyuanshen.modules.MiniProgram.service.MPService;
import com.fuyuanshen.modules.security.config.SecurityProperties;
import com.fuyuanshen.modules.security.security.TokenProvider;
import com.fuyuanshen.modules.security.service.OnlineUserService;
import com.fuyuanshen.modules.security.service.dto.AuthUserDto;
import com.fuyuanshen.modules.security.service.dto.AuthorityDto;
import com.fuyuanshen.modules.security.service.dto.JwtUserDto;
import com.fuyuanshen.modules.system.domain.User;
import com.fuyuanshen.modules.system.service.DeviceService;
import com.fuyuanshen.modules.utils.ResponseVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author: 默苍璃
* @date: 2025-06-2313:56
*/
@Slf4j
@RestController
@RequestMapping("/mp")
@RequiredArgsConstructor
@Api(tags = "小程序:相关接口")
public class MPController {
private final TokenProvider tokenProvider;
private final SecurityProperties properties;
private final OnlineUserService onlineUserService;
private final DeviceService deviceService;
private final MPService mpService;
@Log("小程序用户登录")
@ApiOperation("小程序登录授权")
@AnonymousPostMapping(value = "/login")
public ResponseEntity<Object> login(@RequestBody AuthUserDto authUser, HttpServletRequest request) throws Exception {
// 获取用户信息
User user = new User();
user.setUsername("MP");
user.setPassword("MP");
AuthorityDto authorityDto = new AuthorityDto();
authorityDto.setAuthority("MP");
List<AuthorityDto> authorityDtos = new ArrayList<>();
authorityDtos.add(authorityDto);
user.setPhone(authUser.getPhoneNumber());
JwtUserDto jwtUser = new JwtUserDto(null, user, null, authorityDtos);
Authentication authentication = new UsernamePasswordAuthenticationToken(jwtUser, null, authorityDtos);
SecurityContextHolder.getContext().setAuthentication(authentication);
// 生成令牌
String token = tokenProvider.createToken(jwtUser);
// 返回 token 与 用户信息
Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
put("token", properties.getTokenStartWith() + token);
put("user", jwtUser);
}};
// 保存在线信息
onlineUserService.save(jwtUser, token, request);
// 返回登录信息
return ResponseEntity.ok(authInfo);
}
@GetMapping("/queryDevice")
@ApiOperation("是否存在设备MAC号")
public ResponseVO<Boolean> queryDevice(@ApiParam("设备mac值") String mac) {
return ResponseVO.success(mpService.queryDevice(mac));
}
}

View File

@ -0,0 +1,9 @@
package com.fuyuanshen.modules.MiniProgram.service;
/**
* @author: 默苍璃
* @date: 2025-06-2314:56
*/
public interface MPService {
Boolean queryDevice(String mac);
}

View File

@ -0,0 +1,43 @@
package com.fuyuanshen.modules.MiniProgram.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fuyuanshen.modules.MiniProgram.service.MPService;
import com.fuyuanshen.modules.system.domain.Device;
import com.fuyuanshen.modules.system.mapper.DeviceMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author: 默苍璃
* @date: 2025-06-2314:56
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class MPServiceImpl implements MPService {
private final DeviceMapper deviceMapper;
/**
* 查询设备MAC号
*
* @param mac
* @return
*/
@Override
public Boolean queryDevice(String mac) {
QueryWrapper<Device> wrapper = new QueryWrapper<>();
wrapper.eq("device_mac", mac);
List<Device> deviceList = deviceMapper.selectList(wrapper);
if (CollectionUtil.isNotEmpty(deviceList)) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,33 @@
package com.fuyuanshen.modules.mqtt.config;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
/**
* @Author: HarryLin
* @Date: 2025/3/20 14:40
* @Company: 北京红山信息科技研究院有限公司
* @Email: linyun@***.com.cn
**/
@Configuration
public class MqttConfiguration {
@Autowired
private MqttPropertiesConfig mqttPropertiesConfig;
/** 创建连接工厂 **/
@Bean
public MqttPahoClientFactory mqttPahoClientFactory(){
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(true); //设置新会话
options.setUserName(mqttPropertiesConfig.getUsername());
options.setPassword(mqttPropertiesConfig.getPassword().toCharArray());
options.setServerURIs(new String[]{mqttPropertiesConfig.getUrl()});
factory.setConnectionOptions(options);
return factory;
}
}

View File

@ -0,0 +1,17 @@
package com.fuyuanshen.modules.mqtt.config;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.handler.annotation.Header;
/**
* @Author: HarryLin
* @Date: 2025/3/20 17:06
* @Company: 北京红山信息科技研究院有限公司
* @Email: linyun@***.com.cn
**/
@MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
public interface MqttGateway {
public abstract void sendMsgToMqtt(@Header(value = MqttHeaders.TOPIC) String topic, String payload);
public abstract void sendMsgToMqtt(@Header(value = MqttHeaders.TOPIC) String topic, @Header(value = MqttHeaders.QOS) int qos, String payload );
}

View File

@ -0,0 +1,60 @@
package com.fuyuanshen.modules.mqtt.config;
import com.fuyuanshen.modules.mqtt.receiver.ReceiverMessageHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.core.MessageProducer;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
/**
* @Author: HarryLin
* @Date: 2025/3/20 14:54
* @Company: 北京红山信息科技研究院有限公司
* @Email: linyun@***.com.cn
**/
@Configuration
public class MqttInboundConfiguration {
@Autowired
private MqttPropertiesConfig mqttPropertiesConfig;
@Autowired
private MqttPahoClientFactory mqttPahoClientFactory;
@Autowired
private ReceiverMessageHandler receiverMessageHandler;
//消息通道
@Bean
public MessageChannel messageInboundChannel(){
return new DirectChannel();
}
/**
* 配置入站适配器
* 作用: 设置订阅主题,以及指定消息的通道 等相关属性
* */
@Bean
public MessageProducer messageProducer(){
MqttPahoMessageDrivenChannelAdapter mqttPahoMessageDrivenChannelAdapter = new MqttPahoMessageDrivenChannelAdapter(
mqttPropertiesConfig.getUrl(),
mqttPropertiesConfig.getSubClientId(),
mqttPahoClientFactory,
mqttPropertiesConfig.getSubTopic().split(",")
);
mqttPahoMessageDrivenChannelAdapter.setQos(1);
mqttPahoMessageDrivenChannelAdapter.setConverter(new DefaultPahoMessageConverter());
mqttPahoMessageDrivenChannelAdapter.setOutputChannel(messageInboundChannel());
return mqttPahoMessageDrivenChannelAdapter;
}
/** 指定处理消息来自哪个通道 */
@Bean
@ServiceActivator(inputChannel = "messageInboundChannel")
public MessageHandler messageHandler(){
return receiverMessageHandler;
}
}

View File

@ -0,0 +1,50 @@
package com.fuyuanshen.modules.mqtt.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
/**
* @Author: HarryLin
* @Date: 2025/3/20 15:46
* @Company: 北京红山信息科技研究院有限公司
* @Email: linyun@***.com.cn
**/
@Configuration
@Slf4j
public class MqttOutboundConfiguration {
@Autowired
private MqttPropertiesConfig mqttPropertiesConfig;
@Autowired
private MqttPahoClientFactory mqttPahoClientFactory;
// 消息通道
@Bean
public MessageChannel mqttOutboundChannel(){
return new DirectChannel();
}
/** 配置出站消息处理器 */
@Bean
@ServiceActivator(inputChannel = "mqttOutboundChannel") // 指定处理器针对哪个通道的消息进行处理
public MessageHandler mqttOutboundMessageHandler(){
MqttPahoMessageHandler mqttPahoMessageHandler = new MqttPahoMessageHandler(
mqttPropertiesConfig.getUrl(),
mqttPropertiesConfig.getPubClientId(),
mqttPahoClientFactory
);
mqttPahoMessageHandler.setDefaultQos(1);
mqttPahoMessageHandler.setDefaultTopic("worker/location");
mqttPahoMessageHandler.setAsync(true);
return mqttPahoMessageHandler;
}
}

View File

@ -0,0 +1,24 @@
package com.fuyuanshen.modules.mqtt.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @Author: HarryLin
* @Date: 2025/3/20 14:32
* @Company: 北京红山信息科技研究院有限公司
* @Email: linyun@***.com.cn
**/
@Data
@ConfigurationProperties(prefix = "mqtt")
@Component
public class MqttPropertiesConfig {
private String username;
private String password;
private String url;
private String subClientId;
private String subTopic;
private String pubClientId;
private String pubTopic;
}

View File

@ -0,0 +1,25 @@
package com.fuyuanshen.modules.mqtt.publish;
import com.fuyuanshen.annotation.rest.AnonymousGetMapping;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/device")
@Slf4j
public class DeviceDataController {
@Autowired
private MqttClientTest mqttClientTest;
// @PostMapping("/{deviceId}/command")
@AnonymousGetMapping(value = "/test/command")
public ResponseEntity<String> sendCommand() {
mqttClientTest.sendMsg();
return ResponseEntity.ok("success");
}
}

View File

@ -0,0 +1,22 @@
package com.fuyuanshen.modules.mqtt.publish;
import com.fuyuanshen.modules.mqtt.config.MqttGateway;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class MqttClientTest {
@Autowired
private MqttGateway mqttGateway;
public void sendMsg() {
mqttGateway.sendMsgToMqtt("worker/location/1", "hello mqtt spring boot");
log.info("message is send");
mqttGateway.sendMsgToMqtt("worker/alert/2", "hello mqtt spring boot2");
log.info("message is send2");
}
}

View File

@ -0,0 +1,25 @@
package com.fuyuanshen.modules.mqtt.publish;
import com.fuyuanshen.modules.mqtt.config.MqttGateway;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Service;
/**
* @Author: HarryLin
* @Date: 2025/3/20 16:16
* @Company: 北京红山信息科技研究院有限公司
* @Email: linyun@***.com.cn
**/
@Service
public class MqttMessageSender {
@Autowired
private MqttGateway mqttGateway;
public void sendMsg(@Header(value = MqttHeaders.TOPIC) String topic, String payload) {
mqttGateway.sendMsgToMqtt(topic,payload);
}
public void sendMsg(@Header(value = MqttHeaders.TOPIC) String topic, @Header(value = MqttHeaders.QOS) int qos, String payload) {
mqttGateway.sendMsgToMqtt(topic,qos,payload);
}
}

View File

@ -0,0 +1,31 @@
package com.fuyuanshen.modules.mqtt.receiver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.MessagingException;
import org.springframework.stereotype.Service;
import java.util.Objects;
/**
* @Author: HarryLin
* @Date: 2025/3/20 15:24
* @Company: 北京红山信息科技研究院有限公司
* @Email: linyun@***.com.cn
**/
@Service
@Slf4j
public class ReceiverMessageHandler implements MessageHandler {
@Override
public void handleMessage(Message<?> message) throws MessagingException{
Object payload = message.getPayload();
MessageHeaders headers = message.getHeaders();
String receivedTopic = Objects.requireNonNull(headers.get("mqtt_receivedTopic")).toString();
String receivedQos = Objects.requireNonNull(headers.get("mqtt_receivedQos")).toString();
String timestamp = Objects.requireNonNull(headers.get("timestamp")).toString();
log.info("MQTT payload= {} \n receivedTopic = {} \n receivedQos = {} \n timestamp = {}"
,payload,receivedTopic,receivedQos,timestamp);
}
}

View File

@ -0,0 +1,25 @@
package com.fuyuanshen.modules.security.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class DeviceDataService {
@ServiceActivator(inputChannel = "mqttInputChannel")
public void handleDeviceData(Message<?> message) {
String topic = message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC).toString();
String payload = message.getPayload().toString();
// 解析设备数据
if (topic.startsWith("device/data/")) {
log.info("Received device data device/data/: {} {}", topic, payload);
}
}
}

View File

@ -31,7 +31,7 @@ import org.springframework.context.annotation.Configuration;
public class LoginProperties {
/**
* 账号单用户 登录
* 账号单用户
*/
private boolean singleLogin = false;

View File

@ -55,6 +55,11 @@ public class SecurityProperties {
*/
private String onlineKey;
/**
* app在线用户
*/
private String appOnlineKey;
/**
* 验证码 key
*/

View File

@ -17,6 +17,7 @@ package com.fuyuanshen.modules.security.rest;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.digest.MD5;
import com.fuyuanshen.annotation.Log;
import com.fuyuanshen.annotation.rest.AnonymousDeleteMapping;
import com.fuyuanshen.annotation.rest.AnonymousGetMapping;
@ -31,6 +32,7 @@ import com.fuyuanshen.modules.security.security.app.AppTokenProvider;
import com.fuyuanshen.modules.security.service.OnlineUserService;
import com.fuyuanshen.modules.security.service.UserDetailsServiceImpl;
import com.fuyuanshen.modules.security.service.dto.app.AppAuthUserDto;
import com.fuyuanshen.modules.security.service.dto.app.AppJwtUserDto;
import com.fuyuanshen.modules.security.service.dto.app.AppRoleDto;
import com.fuyuanshen.modules.security.service.dto.AuthUserDto;
import com.fuyuanshen.modules.security.service.dto.JwtUserDto;
@ -55,6 +57,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
@ -89,7 +92,6 @@ public class AuthController {
private final UserDetailsServiceImpl userDetailsService;
private final APPUserMapper appUserMapper;
private final APPUserService appUserService;
@Log("用户登录")
@ApiOperation("登录授权")
@AnonymousPostMapping(value = "/login")
@ -141,16 +143,10 @@ public class AuthController {
}
@Log("app用户登录")
@ApiOperation("app用户登录")
@AnonymousPostMapping(value = "/app/login")
public ResponseEntity<Object> APPLogin(@Validated @RequestBody AppAuthUserDto authUser, HttpServletRequest request) throws Exception {
public ResponseVO<Object> APPLogin(@Validated @RequestBody AppAuthUserDto authUser, HttpServletRequest request) throws Exception {
// 1. 构建查询参数
APPUserQuery appUserQuery = new APPUserQuery();
@ -172,16 +168,15 @@ public class AuthController {
throw new BadRequestException("登录密码错误");
}
// 4. 加载用户详情
JwtUserDto jwtUser = userDetailsService.loadUserByUsername(appUser.getUsername(),appUser.getUserType());
JwtUserDto jwtUser = userDetailsService.loadUserByAppUsername(appUser.getUsername());
// 5. 创建认证信息
Authentication authentication = new UsernamePasswordAuthenticationToken(jwtUser, null, jwtUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
// 6. 生成 Token
String token = appTokenProvider.createToken(jwtUser);
String token = appTokenProvider.createAppToken(jwtUser);
// 7. 获取角色权限
//Integer optLevel = appUserService.selectRoleByUserLevel(appUser.getRoles());
@ -198,10 +193,10 @@ public class AuthController {
}
// 10. 记录在线状态
onlineUserService.save(jwtUser, token, request);
onlineUserService.saveAppOnlineUser(jwtUser, token, request);
// 11. 返回结果
return ResponseEntity.ok(authInfo);
return ResponseVO.success(authInfo);
}

View File

@ -27,6 +27,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Set;
@ -45,8 +46,8 @@ public class OnlineController {
@ApiOperation("查询在线用户")
@GetMapping
@PreAuthorize("@el.check()")
public ResponseEntity<PageResult<OnlineUserDto>> queryOnlineUser(String username, Pageable pageable){
return new ResponseEntity<>(onlineUserService.getAll(username, pageable),HttpStatus.OK);
public ResponseEntity<PageResult<OnlineUserDto>> queryOnlineUser(String username, Pageable pageable) {
return new ResponseEntity<>(onlineUserService.getAll(username, pageable), HttpStatus.OK);
}
@ApiOperation("导出数据")
@ -67,4 +68,5 @@ public class OnlineController {
}
return new ResponseEntity<>(HttpStatus.OK);
}
}

View File

@ -30,6 +30,7 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.security.Key;
@ -75,9 +76,10 @@ public class TokenProvider implements InitializingBean {
claims.put(AUTHORITIES_UID_KEY, user.getUser().getId());
// 设置UUID确保每次Token不一样
claims.put(AUTHORITIES_UUID_KEY, IdUtil.simpleUUID());
claims.put("userType","0");//0 系统登录 1 APP登录
return jwtBuilder
.setClaims(claims)
.setSubject(user.getUsername())
.setSubject(user.getUser().getUsername())
.compact();
}
@ -134,6 +136,16 @@ public class TokenProvider implements InitializingBean {
return properties.getOnlineKey() + claims.getSubject() + ":" + getId(token);
}
/**
* 获取app登录用户RedisKey
* @param token /
* @return key
*/
public String appLoginKey(String token) {
Claims claims = getClaims(token);
return properties.getAppOnlineKey() + claims.getSubject() + ":" + getId(token);
}
/**
* 获取会话编号
* @param token /

View File

@ -20,6 +20,7 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.fuyuanshen.modules.security.config.SecurityProperties;
import com.fuyuanshen.modules.security.service.dto.JwtUserDto;
import com.fuyuanshen.modules.security.service.dto.app.AppJwtUserDto;
import com.fuyuanshen.utils.RedisUtils;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
@ -69,6 +70,7 @@ public class AppTokenProvider implements InitializingBean {
/**
* 创建Token 设置永不过期,
* Token 的时间有效性转到Redis 维护
*
* @param user /
* @return /
*/
@ -76,12 +78,12 @@ public class AppTokenProvider implements InitializingBean {
// 设置参数
Map<String, Object> claims = new HashMap<>(6);
// 设置用户ID
// claims.put(AUTHORITIES_UID_KEY, user.getAppUser().getId());
if (user.getAppUser() != null){
claims.put(AUTHORITIES_UID_KEY, user.getAppUser().getId());
}else {
claims.put(AUTHORITIES_UID_KEY, 0);
}
claims.put(AUTHORITIES_UID_KEY, user.getAppUser().getId());
// if (user.getAppUser() != null){
// claims.put(AUTHORITIES_UID_KEY, user.getAppUser().getId());
// }else {
// claims.put(AUTHORITIES_UID_KEY, 0);
// }
// 设置UUID确保每次Token不一样
claims.put(AUTHORITIES_UUID_KEY, IdUtil.simpleUUID());
return jwtBuilder
@ -90,6 +92,33 @@ public class AppTokenProvider implements InitializingBean {
.compact();
}
/**
* APP创建Token 设置永不过期,
* Token 的时间有效性转到Redis 维护
*
* @param user /
* @return /
*/
public String createAppToken(JwtUserDto user) {
// 设置参数
Map<String, Object> claims = new HashMap<>(6);
// 设置用户ID
claims.put(AUTHORITIES_UID_KEY, user.getAppUser().getId());
// if (user.getAppUser() != null){
// claims.put(AUTHORITIES_UID_KEY, user.getAppUser().getId());
// }else {
// claims.put(AUTHORITIES_UID_KEY, 0);
// }
// 设置UUID确保每次Token不一样
claims.put(AUTHORITIES_UUID_KEY, IdUtil.simpleUUID());
claims.put("userType", "1");// 0 系统登录 1 APP登录
return jwtBuilder
.setClaims(claims)
.setSubject(user.getAppUser().getUsername())
.compact();
}
/**
* 依据Token 获取鉴权信息
*
@ -135,6 +164,7 @@ public class AppTokenProvider implements InitializingBean {
/**
* 获取登录用户RedisKey
*
* @param token /
* @return key
*/
@ -145,6 +175,7 @@ public class AppTokenProvider implements InitializingBean {
/**
* 获取会话编号
*
* @param token /
* @return /
*/

View File

@ -15,6 +15,7 @@
*/
package com.fuyuanshen.modules.security.service;
import com.fuyuanshen.modules.security.service.dto.app.AppJwtUserDto;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import com.fuyuanshen.modules.security.security.TokenProvider;
@ -45,8 +46,6 @@ public class OnlineUserService {
private final RedisUtils redisUtils;
/**
* 保存在线用户信息
* @param jwtUserDto /
@ -54,22 +53,36 @@ public class OnlineUserService {
* @param request /
*/
public void save(JwtUserDto jwtUserDto, String token, HttpServletRequest request){
// String dept = jwtUserDto.getUser().getDept() == null ? null : jwtUserDto.getUser().getDept().getName();
String dept = null;
if (jwtUserDto.getUser() != null) {
dept = jwtUserDto.getUser().getDept() == null ? null : jwtUserDto.getUser().getDept().getName();
}else {
dept= jwtUserDto.getAppUser().getDept() == null ? null : jwtUserDto.getAppUser().getDept().getName();
}
String dept = jwtUserDto.getUser().getDept() == null ? null : jwtUserDto.getUser().getDept().getName();
String ip = StringUtils.getIp(request);
String id = tokenProvider.getId(token);
String browser = StringUtils.getBrowser(request);
String address = StringUtils.getCityInfo(ip);
OnlineUserDto onlineUserDto = null;
try {
onlineUserDto = new OnlineUserDto(id, jwtUserDto.getUsername(), jwtUserDto.getAppUser().getNickName(), dept, browser , ip, address, EncryptUtils.desEncrypt(token), new Date());
onlineUserDto = new OnlineUserDto(id, jwtUserDto.getUsername(), jwtUserDto.getUser().getNickName(), dept, browser , ip, address, EncryptUtils.desEncrypt(token), new Date());
} catch (Exception e) {
log.error(e.getMessage(),e);
}
String loginKey = tokenProvider.loginKey(token);
redisUtils.set(loginKey, onlineUserDto, properties.getTokenValidityInSeconds(), TimeUnit.MILLISECONDS);
}
/**
* 保存在线用户信息
* @param jwtUserDto /
* @param token /
* @param request /
*/
public void saveAppOnlineUser(JwtUserDto jwtUserDto, String token, HttpServletRequest request){
String dept = jwtUserDto.getAppUser().getDept() == null ? null : jwtUserDto.getAppUser().getDept().getName();
String ip = StringUtils.getIp(request);
String id = tokenProvider.getId(token);
String browser = StringUtils.getBrowser(request);
String address = StringUtils.getCityInfo(ip);
OnlineUserDto onlineUserDto = null;
try {
onlineUserDto = new OnlineUserDto(id, jwtUserDto.getAppUser().getUsername(), jwtUserDto.getAppUser().getNickName(), dept, browser , ip, address, EncryptUtils.desEncrypt(token), new Date());
} catch (Exception e) {
log.error(e.getMessage(),e);
}

View File

@ -18,11 +18,13 @@ package com.fuyuanshen.modules.security.service;
import cn.hutool.core.util.RandomUtil;
import com.fuyuanshen.modules.security.config.LoginProperties;
import com.fuyuanshen.modules.security.service.dto.JwtUserDto;
import com.fuyuanshen.modules.security.service.dto.app.AppJwtUserDto;
import com.fuyuanshen.utils.RedisUtils;
import com.fuyuanshen.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
@ -38,8 +40,10 @@ public class UserCacheManager {
@Value("${login.user-cache.idle-time}")
private long idleTime;
/**
* 返回用户缓存
*
* @param userName 用户名
* @return JwtUserDto
*/
@ -48,24 +52,32 @@ public class UserCacheManager {
userName = StringUtils.lowerCase(userName);
if (StringUtils.isNotEmpty(userName)) {
// 获取数据
try {
JwtUserDto jwtUserDto = redisUtils.get(LoginProperties.cacheKey + userName, JwtUserDto.class);
if (jwtUserDto != null){
jwtUserDto.getUsername();
}
return jwtUserDto;
} catch (Exception e) {
// redisUtils.del(LoginProperties.cacheKey + userName);
cleanUserCache(userName);
return null;
}
}
return redisUtils.get(LoginProperties.cacheKey + userName, JwtUserDto.class);
}
return null;
}
/**
* 添加缓存到Redis
* 返回用户缓存
*
* @param userName 用户名
* @return JwtUserDto
*/
public JwtUserDto getAppUserCache(String userName) {
// 转小写
userName = StringUtils.lowerCase(userName);
if (StringUtils.isNotEmpty(userName)) {
// 获取数据
return redisUtils.get(LoginProperties.cacheKey_app + userName, JwtUserDto.class);
}
return null;
}
/**
* 添加缓存到Redis
*
* @param userName 用户名
*/
@Async
@ -82,6 +94,7 @@ public class UserCacheManager {
/**
* 清理用户缓存信息
* 用户信息变更时
*
* @param userName 用户名
*/
@Async
@ -96,17 +109,18 @@ public class UserCacheManager {
/**
* 返回用户缓存
*
* @param userName 用户名
* @return JwtUserDto
*/
public JwtUserDto getUserCache(String userName, Integer userType) {
public AppJwtUserDto getUserCache(String userName, Integer userType) {
// 转小写
userName = StringUtils.lowerCase(userName);
if (StringUtils.isNotEmpty(userName)) {
// 获取数据
try {
JwtUserDto jwtUserDto = redisUtils.get(LoginProperties.cacheKey_app + userName, JwtUserDto.class);
if (jwtUserDto != null){
AppJwtUserDto jwtUserDto = redisUtils.get(LoginProperties.cacheKey_app + userName, AppJwtUserDto.class);
if (jwtUserDto != null) {
jwtUserDto.getUsername();
}
return jwtUserDto;
@ -121,11 +135,28 @@ public class UserCacheManager {
}
/**
* 添加缓存到Redis
* 添加缓存到Redis
*
* @param userName 用户名
*/
@Async
public void addUserCache(String userName, JwtUserDto user,Integer userType) {
public void addUserCache(String userName, JwtUserDto user, Integer userType) {
// 转小写
userName = StringUtils.lowerCase(userName);
if (StringUtils.isNotEmpty(userName)) {
// 添加数据, 避免数据同时过期
long time = idleTime + RandomUtil.randomInt(900, 1800);
redisUtils.set(LoginProperties.cacheKey_app + userName, user, time);
}
}
/**
* App用户添加缓存到Redis
*
* @param userName 用户名
*/
@Async
public void addAppUserCache(String userName, JwtUserDto user) {
// 转小写
userName = StringUtils.lowerCase(userName);
if (StringUtils.isNotEmpty(userName)) {
@ -138,10 +169,11 @@ public class UserCacheManager {
/**
* 清理用户缓存信息
* 用户信息变更时
*
* @param userName 用户名
*/
@Async
public void cleanUserCache(String userName,Integer userType) {
public void cleanUserCache(String userName, Integer userType) {
// 转小写
userName = StringUtils.lowerCase(userName);
if (StringUtils.isNotEmpty(userName)) {

View File

@ -15,16 +15,19 @@
*/
package com.fuyuanshen.modules.security.service;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.fuyuanshen.exception.BadRequestException;
import com.fuyuanshen.modules.security.service.dto.AuthorityDto;
import com.fuyuanshen.modules.security.service.dto.JwtUserDto;
import com.fuyuanshen.modules.system.domain.User;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import com.fuyuanshen.modules.system.service.DataService;
import com.fuyuanshen.modules.system.service.RoleService;
import com.fuyuanshen.modules.system.service.UserService;
import com.fuyuanshen.utils.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
@ -44,9 +47,30 @@ public class UserDetailsServiceImpl implements UserDetailsService {
private final DataService dataService;
private final UserCacheManager userCacheManager;
private final static String APP_USER_TYPE = "1"; // app用户类型
private final static String SYSTEM_USER_TYPE = "0"; // 系统用户类型
@Override
public JwtUserDto loadUserByUsername(String username) {
if(SecurityUtils.getToken() != null){
JWT jwt = JWTUtil.parseToken(SecurityUtils.getToken());
String userType = jwt.getPayload("userType").toString();
if(APP_USER_TYPE.equals(userType)){
return loadUserByAppUsername(username);
}else{
return loadSystemUserByUsername(username);
}
}else{
return loadSystemUserByUsername(username);
}
}
/**
* 加载系统用户详情信息
* @param username
* @return
*/
private JwtUserDto loadSystemUserByUsername(String username) {
JwtUserDto jwtUserDto = userCacheManager.getUserCache(username);
if (jwtUserDto == null) {
User user = userService.getLoginData(username);
@ -59,7 +83,7 @@ public class UserDetailsServiceImpl implements UserDetailsService {
// 获取用户的权限
List<AuthorityDto> authorities = roleService.buildPermissions(user);
// 初始化JwtUserDto
jwtUserDto = new JwtUserDto(user, null,dataService.getDeptIds(user), authorities);
jwtUserDto = new JwtUserDto(null,user, dataService.getDeptIds(user), authorities);
// 添加缓存数据
userCacheManager.addUserCache(username, jwtUserDto);
}
@ -67,11 +91,15 @@ public class UserDetailsServiceImpl implements UserDetailsService {
return jwtUserDto;
}
/**
* 加载app用户详情信息
* @param username
* @return
*/
public JwtUserDto loadUserByAppUsername(String username) {
public JwtUserDto loadUserByUsername(String username, Integer userType) {
JwtUserDto jwtUserDto = userCacheManager.getUserCache(username , userType);
JwtUserDto jwtUserDto = userCacheManager.getAppUserCache(username);
if (jwtUserDto == null) {
APPUser user = userService.appGetLoginData(username);
if (user == null) {
throw new BadRequestException("用户不存在");
@ -82,11 +110,13 @@ public class UserDetailsServiceImpl implements UserDetailsService {
// 获取用户的权限
List<AuthorityDto> authorities = roleService.appBuildPermissions(user);
// 初始化JwtUserDto
jwtUserDto = new JwtUserDto(null,user, dataService.getDeptIds(user), authorities);
jwtUserDto = new JwtUserDto(user,null, dataService.getDeptIds(user), authorities);
// 添加缓存数据
userCacheManager.addUserCache(username, jwtUserDto, userType);
userCacheManager.addAppUserCache(username, jwtUserDto);
}
}
return jwtUserDto;
}
}

View File

@ -32,8 +32,8 @@ public class AuthUserDto {
@ApiModelProperty(value = "用户名")
private String username;
@ApiModelProperty(value = "手机号APP登录")
private String phoneNumber;
@ApiModelProperty(value = "手机号APP/小程序 登录)")
private Long phoneNumber;
@NotBlank
@ApiModelProperty(value = "密码")

View File

@ -20,12 +20,10 @@ import com.fuyuanshen.modules.system.domain.app.APPUser;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import com.fuyuanshen.modules.system.domain.User;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@ -34,62 +32,37 @@ import java.util.stream.Collectors;
* @author Zheng Jie
* @date 2018-11-23
*/
@NoArgsConstructor(force = true)
@Getter
@Setter
public class JwtUserDto implements UserDetails, java.io.Serializable {
@AllArgsConstructor
public class JwtUserDto implements UserDetails {
@ApiModelProperty(value = "app用户")
private final APPUser appUser;
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "用户")
private final User user;
@ApiModelProperty(value = "App用户")
private final APPUser appUser;
@ApiModelProperty(value = "数据权限")
private final List<Long> dataScopes;
@ApiModelProperty(value = "角色")
private final List<AuthorityDto> authorities;
private String username;
private String password;
private boolean enabled = true;
public Set<String> getRoles() {
if (authorities== null){
return Collections.emptySet();
}
return authorities.stream().map(AuthorityDto::getAuthority).collect(Collectors.toSet());
}
@Override
//@JSONField(serialize = false)
@JSONField(serialize = false)
public String getPassword() {
if (appUser != null) {
return appUser.getPassword();
}
return user.getPassword();
}
public JwtUserDto(User user, APPUser appUser, List<Long> dataScopes, List<AuthorityDto> authorities) {
this.user = user;
this.appUser = appUser;
this.dataScopes = dataScopes;
this.authorities = authorities;
if (user != null) {
this.username = user.getUsername();
this.password = user.getPassword();
this.enabled = user.getEnabled();
} else if (appUser != null) {
this.username = appUser.getUsername();
this.password = appUser.getPassword();
this.enabled = appUser.getEnabled();
}
}
@Override
//@JSONField(serialize = false)
@JSONField(serialize = false)
public String getUsername() {
if (appUser != null) {
return appUser.getUsername();
@ -97,35 +70,30 @@ public class JwtUserDto implements UserDetails, java.io.Serializable {
return user.getUsername();
}
//@JSONField(serialize = false)
public String appGetUsername() {
return appUser.getUsername();
}
//@JSONField(serialize = false)
@JSONField(serialize = false)
@Override
public boolean isAccountNonExpired() {
return true;
}
//@JSONField(serialize = false)
@JSONField(serialize = false)
@Override
public boolean isAccountNonLocked() {
return true;
}
//@JSONField(serialize = false)
@JSONField(serialize = false)
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
//@JSONField(serialize = false)
@JSONField(serialize = false)
public boolean isEnabled() {
return enabled;
if (appUser != null) {
return appUser.getEnabled();
}
return user.getEnabled();
}
}

View File

@ -0,0 +1,28 @@
package com.fuyuanshen.modules.system.constant;
/**
* 响应消息常量类
*
* @author: 默苍璃
* @date: 2025-06-2117:21
*/
public class ResponseMessageConstants {
/**
* 删除操作成功提示
*/
public static final String DELETE_SUCCESS = "删除成功!";
/**
* 新增操作成功提示
*/
public static final String SAVE_SUCCESS = "新增成功!";
/**
* 更新操作成功提示
*/
public static final String UPDATE_SUCCESS = "更新成功!";
// 可根据业务需求继续扩展其他常用提示信息
}

View File

@ -26,6 +26,10 @@ public class Device extends BaseEntity implements Serializable {
@ApiModelProperty(value = "ID")
private Long id;
@ApiModelProperty(value = "设备记录ID")
@TableField(exist = false)
private Long assignId;
@ApiModelProperty(value = "设备类型")
private Long deviceType;
@ -35,9 +39,17 @@ public class Device extends BaseEntity implements Serializable {
@ApiModelProperty(value = "客户号")
private Long customerId;
/**
* 当前所有者
* current_owner_id
*/
@ApiModelProperty(value = "当前所有者")
private Long currentOwnerId;
/**
* 原始所有者(创建者)
* original_owner_id
*/
@ApiModelProperty(value = "原始所有者(创建者)")
private Long originalOwnerId;

View File

@ -5,9 +5,12 @@ import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Date;
/**
* 设备分配记录表
*
* @TableName device_assignments
*/
@TableName(value = "device_assignments")
@ -35,10 +38,24 @@ public class DeviceAssignments {
*/
private Long toCustomerId;
/**
* 分配者
* assigner_name
*/
private Long assignerId;
private String assignerName;
/**
* 接收者
* assignee_name
*/
private Long assigneeId;
private String assigneeName;
/**
* 分配时间
*/
private Date assignedAt;
private LocalDateTime assignedAt;
/**
* 0 未授权
@ -47,48 +64,23 @@ public class DeviceAssignments {
*/
private Integer deviceTypeGranted;
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null) {
return false;
}
if (getClass() != that.getClass()) {
return false;
}
DeviceAssignments other = (DeviceAssignments) that;
return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId())) && (this.getDeviceId() == null ? other.getDeviceId() == null : this.getDeviceId().equals(other.getDeviceId())) && (this.getFromCustomerId() == null ? other.getFromCustomerId() == null : this.getFromCustomerId().equals(other.getFromCustomerId())) && (this.getToCustomerId() == null ? other.getToCustomerId() == null : this.getToCustomerId().equals(other.getToCustomerId())) && (this.getAssignedAt() == null ? other.getAssignedAt() == null : this.getAssignedAt().equals(other.getAssignedAt())) && (this.getDeviceTypeGranted() == null ? other.getDeviceTypeGranted() == null : this.getDeviceTypeGranted().equals(other.getDeviceTypeGranted()));
}
/**
* 0 否
* 1 是
* 是否直接分配(用于父级显示)
*/
private Integer direct;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
result = prime * result + ((getDeviceId() == null) ? 0 : getDeviceId().hashCode());
result = prime * result + ((getFromCustomerId() == null) ? 0 : getFromCustomerId().hashCode());
result = prime * result + ((getToCustomerId() == null) ? 0 : getToCustomerId().hashCode());
result = prime * result + ((getAssignedAt() == null) ? 0 : getAssignedAt().hashCode());
result = prime * result + ((getDeviceTypeGranted() == null) ? 0 : getDeviceTypeGranted().hashCode());
return result;
}
/**
* 0 否
* 1 是
* 设备是否有效
*/
private Integer active;
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [");
sb.append("Hash = ").append(hashCode());
sb.append(", id=").append(id);
sb.append(", deviceId=").append(deviceId);
sb.append(", fromCustomerId=").append(fromCustomerId);
sb.append(", toCustomerId=").append(toCustomerId);
sb.append(", assignedAt=").append(assignedAt);
sb.append(", deviceTypeGranted=").append(deviceTypeGranted);
sb.append("]");
return sb.toString();
}
/**
* 分配等级(用于失效)
*/
private String lever;
}

View File

@ -32,7 +32,8 @@ public class DeviceType extends BaseEntity implements Serializable {
private Long customerId;
@ApiModelProperty(value = "创建该类型的客户")
private String ownerCustomerId;
private Long ownerCustomerId;
/**
* 租户ID
*/

View File

@ -26,7 +26,6 @@ public class APPDevice extends BaseEntity implements Serializable {
@ApiModelProperty(value = "ID")
private Long id;
@ApiModelProperty(value = "设备类型")
private Long deviceType;
@ -51,6 +50,9 @@ public class APPDevice extends BaseEntity implements Serializable {
@ApiModelProperty(value = "设备MAC")
private String deviceMac;
@ApiModelProperty(value = "设备IMEI")
private String deviceImei;
@ApiModelProperty(value = "设备SN")
private String deviceSn;
@ -90,6 +92,14 @@ public class APPDevice extends BaseEntity implements Serializable {
@ApiModelProperty(value = "绑定状态")
private Integer bindingStatus;
/**
* 绑定类型
* 0 APP
* 1 小程序
*/
@ApiModelProperty(value = "绑定类型")
private Integer bindingType;
public void copy(Device source) {
BeanUtil.copyProperties(source, this, CopyOptions.create().setIgnoreNullValue(true));

View File

@ -3,10 +3,13 @@ package com.fuyuanshen.modules.system.domain.app;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fuyuanshen.modules.system.domain.DeviceType;
import com.fuyuanshen.base.BaseEntity;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* @Description: 设备类型
* @Author: WY
@ -14,11 +17,40 @@ import lombok.Data;
**/
@Data
@TableName("app_device_type")
public class APPDeviceType extends DeviceType {
public class APPDeviceType extends BaseEntity implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "ID", hidden = true)
private Long id;
@ApiModelProperty(value = "客户号")
private Long customerId;
@ApiModelProperty(value = "创建该类型的客户")
private Long ownerCustomerId;
/**
* 租户ID
*/
// @TableField(value = "tenant_id")
// @ApiModelProperty(hidden = true)
// private Long tenantId;
@NotBlank(message = "设备类型名称不能为空")
@ApiModelProperty(value = "类型名称", required = true)
private String typeName;
@ApiModelProperty(value = "是否支持蓝牙")
private Boolean isSupportBle;
@ApiModelProperty(value = "定位方式", example = "0:无;1:GPS;2:基站;3:wifi;4:北斗")
private String locateMode;
@ApiModelProperty(value = "联网方式", example = "0:无;1:4G;2:WIFI")
private String networkWay;
@ApiModelProperty(value = "通讯方式", example = "0:4G;1:蓝牙")
private String communicationMode;
}

View File

@ -16,14 +16,19 @@
package com.fuyuanshen.modules.system.domain.app;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fuyuanshen.modules.system.domain.Role;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import com.fuyuanshen.base.BaseEntity;
import com.fuyuanshen.utils.enums.DataScopeEnum;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Objects;
import java.util.Set;
/**
* 角色
@ -32,12 +37,49 @@ import javax.validation.constraints.NotNull;
*/
@Getter
@Setter
@TableName("app_sys_role")
public class APPRole extends Role {
@TableName("sys_role")
public class APPRole extends BaseEntity implements Serializable {
@NotNull(groups = {Update.class})
@TableId(value="app_role_id", type = IdType.AUTO)
@TableId(value="role_id", type = IdType.AUTO)
@ApiModelProperty(value = "ID", hidden = true)
private Long id;
@TableField(exist = false)
@ApiModelProperty(value = "用户", hidden = true)
private Set<APPUser> users;
@TableField(exist = false)
@ApiModelProperty(value = "菜单", hidden = true)
private Set<APPMenu> menus;
@NotBlank
@ApiModelProperty(value = "名称", hidden = true)
private String name;
@ApiModelProperty(value = "数据权限,全部 、 本级 、 自定义")
private String dataScope = DataScopeEnum.THIS_LEVEL.getValue();
@ApiModelProperty(value = "级别,数值越小,级别越大")
private Integer level = 3;
@ApiModelProperty(value = "描述")
private String description;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
APPRole role = (APPRole) o;
return Objects.equals(id, role.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}

View File

@ -133,4 +133,8 @@ public class APPUser extends BaseEntity implements Serializable {
@ApiModelProperty(hidden = true)
private Integer userType;
/**
* 地区
*/
private String region;
}

View File

@ -2,7 +2,9 @@ package com.fuyuanshen.modules.system.domain.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
@ -11,12 +13,15 @@ import java.util.List;
* @Date: 2025/5/28
**/
@Data
@Validated
public class CustomerVo {
@ApiModelProperty(value = "客户ID")
@NotNull(message = "客户ID不能为空")
private Long customerId;
@ApiModelProperty(value = "设备ID")
@NotNull(message = "设备ID不能为空")
private List<Long> deviceIds;
}

View File

@ -46,9 +46,9 @@ public class DeviceExcelExportDTO {
@ColumnWidth(20)
private String deviceMac;
@ExcelProperty("设备SN")
@ExcelProperty("设备IMEI")
@ColumnWidth(20)
private String deviceSn;
private String deviceImei;
@ExcelProperty("经度")
private String longitude;

View File

@ -5,6 +5,7 @@ import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import com.alibaba.excel.converters.bytearray.ByteArrayImageConverter;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.fuyuanshen.modules.system.converter.IgnoreFailedImageConverter;
@ -43,6 +44,11 @@ public class DeviceExcelImportDTO {
@ColumnWidth(20)
private String deviceMac;
@ExcelProperty("设备IMEI")
@ColumnWidth(20)
@ApiModelProperty(value = "设备IMEI")
private String deviceImei;
@ExcelProperty("设备SN")
@ColumnWidth(20)
private String deviceSn;

View File

@ -25,6 +25,9 @@ public class DeviceQueryCriteria {
@ApiModelProperty(value = "设备MAC")
private String deviceMac;
@ApiModelProperty(value = "设备IMEI")
private String deviceImei;
@ApiModelProperty(value = "设备SN")
private String deviceSn;
@ -55,4 +58,7 @@ public class DeviceQueryCriteria {
@ApiModelProperty(value = "租户ID")
private Long tenantId;
@ApiModelProperty(value = "通讯方式", example = "0:4G;1:蓝牙")
private Integer communicationMode;
}

View File

@ -0,0 +1,27 @@
package com.fuyuanshen.modules.system.domain.dto.app;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @author: 默苍璃
* @date: 2025-06-1818:36
*/
@Data
public class APPForgotPasswordDTO {
@NotBlank(message = "手机号不能为空")
@ApiModelProperty(value = "手机号APP登录")
private String phoneNumber;
@NotBlank(message = "密码不能为空")
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "验证码")
@NotBlank(message = "验证码不能为空")
private String verificationCode;
}

View File

@ -0,0 +1,32 @@
package com.fuyuanshen.modules.system.domain.dto.app;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotBlank;
/**
* @author: 默苍璃
* @date: 2025-06-1818:36
*/
@Data
public class APPUpdateUserDTO {
@ApiModelProperty(value = "ID", hidden = true)
private Long id;
@ApiModelProperty(value = "用户昵称")
private String nickName;
@ApiModelProperty(value = "地区")
private String region;
@ApiModelProperty(value = "用户性别")
private String gender;
@ApiModelProperty(value = "头像图片")
private MultipartFile file;
}

View File

@ -0,0 +1,40 @@
package com.fuyuanshen.modules.system.domain.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author: 默苍璃
* @date: 2025-06-1211:34
*/
@Data
@JsonInclude(JsonInclude.Include.ALWAYS)
public class APPUserVo {
@ApiModelProperty(value = "ID")
@JsonProperty("id")
private Long id;
@ApiModelProperty(value = "用户昵称")
@JsonProperty("nickName")
private String nickName;
@ApiModelProperty(value = "用户性别")
@JsonProperty("gender")
private String gender;
@ApiModelProperty(value = "电话号码")
@JsonProperty("phone")
private Long phone;
@ApiModelProperty(value = "头像存储的路径")
@JsonProperty("avatarPath")
private String avatarPath;
@ApiModelProperty(value = "地区")
@JsonProperty("region")
private String region;
}

View File

@ -0,0 +1,45 @@
package com.fuyuanshen.modules.system.enums;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* 通讯方式枚举
*
* @author: 默苍璃
* @date: 2025-06-2414:11
*/
public enum CommunicationModeEnum {
FOUR_G(0, "4G"),
BLUETOOTH(1, "蓝牙");
private final int value;
private final String label;
CommunicationModeEnum(int value, String label) {
this.value = value;
this.label = label;
}
@JsonValue
public int getValue() {
return value;
}
public String getLabel() {
return label;
}
/**
* 根据值获取标签
*/
public static String getLabelByValue(int value) {
for (CommunicationModeEnum mode : values()) {
if (mode.getValue() == value) {
return mode.getLabel();
}
}
return null;
}
}

View File

@ -0,0 +1,44 @@
package com.fuyuanshen.modules.system.enums;
/**
* 设备有效性状态枚举
*
* @author: 默苍璃
* @date: 2025-06-2113:26
*/
public enum DeviceActiveStatusEnum {
INACTIVE(0, "无效"), ACTIVE(1, "有效");
private final Integer code;
private final String description;
DeviceActiveStatusEnum(Integer code, String description) {
this.code = code;
this.description = description;
}
public Integer getCode() {
return code;
}
public String getDescription() {
return description;
}
/**
* 根据 code 获取描述
*
* @param code 状态码
* @return 描述信息
*/
public static String getDescriptionByCode(Integer code) {
for (DeviceActiveStatusEnum status : values()) {
if (status.getCode().equals(code)) {
return status.getDescription();
}
}
return null;
}
}

View File

@ -3,6 +3,7 @@ package com.fuyuanshen.modules.system.listener.excel;
import com.fuyuanshen.modules.system.mapper.DeviceMapper;
import com.fuyuanshen.modules.system.mapper.DeviceTypeMapper;
import com.fuyuanshen.modules.system.mapper.UserMapper;
import com.fuyuanshen.modules.system.service.DeviceAssignmentsService;
import com.fuyuanshen.modules.system.service.DeviceService;
import lombok.*;
import org.springframework.web.multipart.MultipartFile;
@ -19,6 +20,7 @@ import org.springframework.web.multipart.MultipartFile;
public class DeviceImportParams {
private DeviceService deviceService;
private DeviceAssignmentsService deviceAssignmentsService;
private DeviceMapper deviceMapper;
private UserMapper userMapper;
private DeviceTypeMapper deviceTypeMapper;

View File

@ -7,9 +7,13 @@ import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fuyuanshen.modules.system.domain.DeviceAssignments;
import com.fuyuanshen.modules.system.domain.DeviceType;
import com.fuyuanshen.modules.system.domain.User;
import com.fuyuanshen.modules.system.domain.dto.DeviceQueryCriteria;
import com.fuyuanshen.modules.system.enums.DeviceActiveStatusEnum;
import com.fuyuanshen.modules.system.mapper.UserMapper;
import com.fuyuanshen.utils.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import com.fuyuanshen.constants.DeviceConstants;
import com.fuyuanshen.modules.system.domain.Device;
@ -24,6 +28,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.nio.file.Files;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -149,6 +154,7 @@ public class UploadDeviceDataListener implements ReadListener<DeviceExcelImportD
private void processDataRowByRow() {
User currentUser = params.getUserMapper().findByUsername(SecurityUtils.getCurrentUsername());
for (Integer rowIndex : rowIndexList) {
Device device = rowDeviceMap.get(rowIndex);
DeviceExcelImportDTO originalDto = rowDtoMap.get(rowIndex);
@ -171,6 +177,20 @@ public class UploadDeviceDataListener implements ReadListener<DeviceExcelImportD
device.setDeviceType(deviceTypes.get(0).getId());
}
params.getDeviceService().save(device);
// 新增设备类型记录
DeviceAssignments assignments = new DeviceAssignments();
assignments.setDeviceId(device.getId());
assignments.setAssignedAt(LocalDateTime.now());
// 分配者
assignments.setAssignerId(currentUser.getId());
// 接收者
assignments.setAssigneeId(currentUser.getId());
assignments.setActive(DeviceActiveStatusEnum.ACTIVE.getCode());
String lever = currentUser.getId() + ":";
assignments.setLever(lever);
params.getDeviceAssignmentsService().save(assignments);
successCount++;
log.info("行 {} 数据插入成功", rowIndex);
} catch (Exception e) {

View File

@ -0,0 +1,40 @@
/*
* Copyright 2019-2025 Zheng Jie
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.fuyuanshen.modules.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fuyuanshen.modules.system.domain.Role;
import com.fuyuanshen.modules.system.domain.app.APPRole;
import com.fuyuanshen.modules.system.domain.dto.RoleQueryCriteria;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Set;
/**
* @author Zheng Jie
* @date 2023-06-20
*/
@Mapper
public interface APPRoleMapper extends BaseMapper<APPRole> {
List<APPRole> findByUserId(@Param("userId") Long userId);
}

View File

@ -14,4 +14,5 @@ import org.apache.ibatis.annotations.Mapper;
public interface DeviceAssignmentsMapper extends BaseMapper<DeviceAssignments> {
}

View File

@ -19,6 +19,15 @@ import java.util.List;
@Mapper
public interface APPDeviceMapper extends BaseMapper<APPDevice> {
/**
* APP用户设备列表
*
* @param page
* @param criteria
* @return
*/
IPage<APPDevice> appDeviceList(Page<APPDevice> page,@Param("criteria") DeviceQueryCriteria criteria);
/**
* 分页查询APP/小程序设备
@ -27,6 +36,7 @@ public interface APPDeviceMapper extends BaseMapper<APPDevice> {
* @param page
* @return
*/
IPage<APPDevice> queryAll(Page<APPDevice> page, @Param("criteria")DeviceQueryCriteria criteria );
IPage<APPDevice> queryAll(Page<APPDevice> page, @Param("criteria") DeviceQueryCriteria criteria);
}

View File

@ -0,0 +1,28 @@
package com.fuyuanshen.modules.system.mapper.app;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fuyuanshen.modules.system.domain.app.APPDeviceType;
import com.fuyuanshen.modules.system.domain.dto.DeviceQueryCriteria;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* @author 97433
* @description 针对表【app_device_type(设备类型表)】的数据库操作Mapper
* @createDate 2025-06-24 11:16:18
* @Entity system.domain.AppDeviceType
*/
@Mapper
public interface AppDeviceTypeMapper extends BaseMapper<APPDeviceType> {
/**
* 查询设备类型列表
*
* @param criteria 查询条件
* @return 设备类型列表
*/
List<APPDeviceType> appTypeList(DeviceQueryCriteria criteria);
}

View File

@ -2,35 +2,35 @@ package com.fuyuanshen.modules.system.rest;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fuyuanshen.annotation.Log;
import com.fuyuanshen.exception.BadRequestException;
import com.fuyuanshen.modules.system.constant.ResponseMessageConstants;
import com.fuyuanshen.modules.system.constant.UserConstants;
import com.fuyuanshen.modules.system.domain.Device;
import com.fuyuanshen.modules.system.domain.User;
import com.fuyuanshen.modules.system.domain.dto.CustomerVo;
import com.fuyuanshen.modules.system.domain.dto.DeviceExcelImportDTO;
import com.fuyuanshen.modules.system.domain.dto.DeviceForm;
import com.fuyuanshen.modules.system.domain.dto.DeviceQueryCriteria;
import com.fuyuanshen.modules.system.listener.excel.DeviceImportParams;
import com.fuyuanshen.modules.system.listener.excel.UploadDeviceDataListener;
import com.fuyuanshen.modules.system.mapper.DeviceMapper;
import com.fuyuanshen.modules.system.mapper.DeviceTypeMapper;
import com.fuyuanshen.modules.system.mapper.UserMapper;
import com.fuyuanshen.modules.system.service.DeviceAssignmentsService;
import com.fuyuanshen.modules.system.service.DeviceService;
import com.fuyuanshen.modules.system.service.UserService;
import com.fuyuanshen.modules.system.service.impl.app.APPUserServiceImpl;
import com.fuyuanshen.modules.system.service.impl.DeviceExportService;
import com.fuyuanshen.modules.utils.ResponseVO;
import com.fuyuanshen.modules.utils.excel.ImportResult;
import com.fuyuanshen.utils.FileUtil;
import com.fuyuanshen.utils.PageResult;
import com.fuyuanshen.utils.SecurityUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import com.fuyuanshen.annotation.Log;
import com.fuyuanshen.domain.LocalStorage;
import com.fuyuanshen.exception.BadRequestException;
import com.fuyuanshen.modules.system.domain.Device;
import com.fuyuanshen.modules.system.domain.dto.CustomerVo;
import com.fuyuanshen.modules.system.domain.dto.DeviceExcelImportDTO;
import com.fuyuanshen.modules.system.domain.dto.DeviceForm;
import com.fuyuanshen.modules.system.domain.dto.DeviceQueryCriteria;
import com.fuyuanshen.modules.system.listener.excel.UploadDeviceDataListener;
import com.fuyuanshen.modules.system.service.DeviceService;
import com.fuyuanshen.modules.system.service.impl.DeviceExportService;
import com.fuyuanshen.modules.utils.ResponseVO;
import com.fuyuanshen.modules.utils.excel.ImportResult;
import com.fuyuanshen.utils.FileUtil;
import com.fuyuanshen.utils.PageResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
@ -50,7 +50,6 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.List;
import java.util.Map;
/**
* @Description:
@ -65,6 +64,7 @@ import java.util.Map;
public class DeviceController {
private final DeviceService deviceService;
private final DeviceAssignmentsService deviceAssignmentsService;
private final DeviceExportService exportService;
private final UserMapper userMapper;
private final DeviceMapper deviceMapper;
@ -141,12 +141,7 @@ public class DeviceController {
@ApiOperation("分配客户")
@PutMapping(value = "/assignCustomer")
public ResponseVO<Object> assignCustomer(@Validated @RequestBody CustomerVo customerVo) {
try {
deviceService.assignCustomer(customerVo);
} catch (Exception e) {
log.error("updateDevice error: " + e.getMessage());
return ResponseVO.fail(e.getMessage());
}
deviceService.assignCustomer(customerVo);
return ResponseVO.success(null);
}
@ -163,13 +158,9 @@ public class DeviceController {
@ApiOperation("删除设备")
@DeleteMapping(value = "/delete")
public ResponseVO<Object> deleteDevice(@ApiParam(value = "传ID数组[]") @RequestBody List<Long> ids) {
try {
deviceService.deleteAll(ids);
} catch (Exception e) {
log.error("deleteDevice error: " + e.getMessage());
return ResponseVO.fail(e.getMessage());
}
return ResponseVO.success(null);
// deviceService.deleteAll(ids);
deviceService.deleteAssign(ids);
return ResponseVO.success(ResponseMessageConstants.DELETE_SUCCESS);
}
@ -181,7 +172,8 @@ public class DeviceController {
// 只能看到自己的创建的设备,以及被分配的设备。
if (onlineuser.getTenantId() != null && !onlineuser.getTenantId().equals(UserConstants.SUPER_ADMIN_ID)) {
criteria.setTenantId(onlineuser.getTenantId());
// criteria.setTenantId(onlineuser.getTenantId());
criteria.setCurrentOwnerId(onlineuser.getId());
}
exportService.export(deviceService.queryAll(criteria), response);
}
@ -215,29 +207,21 @@ public class DeviceController {
ImportResult result = new ImportResult();
try {
User currentUser = userMapper.findByUsername(SecurityUtils.getCurrentUsername());
DeviceImportParams params = DeviceImportParams.builder().ip(ip).deviceService(deviceService).tenantId(currentUser.getTenantId()).file(file).filePath(filePath).deviceMapper(deviceMapper).deviceTypeMapper(deviceTypeMapper).userId(currentUser.getId()).build();
DeviceImportParams params = DeviceImportParams.builder().ip(ip).deviceService(deviceService).tenantId(currentUser.getTenantId()).file(file).filePath(filePath).deviceMapper(deviceMapper).deviceAssignmentsService(deviceAssignmentsService).deviceTypeMapper(deviceTypeMapper).userId(currentUser.getId()).userMapper(userMapper).build();
// 创建监听器
UploadDeviceDataListener listener = new UploadDeviceDataListener(params);
// 读取Excel
EasyExcel.read(file.getInputStream(), DeviceExcelImportDTO.class, listener).sheet().doRead();
// 获取导入结果
result = listener.getImportResult();
// 设置响应消息
String message = String.format("成功导入 %d 条数据,失败 %d 条", result.getSuccessCount(), result.getFailureCount());
// 返回带有正确泛型的响应
return ResponseVO.<ImportResult>success(message, result);
} catch (Exception e) {
log.error("导入设备数据出错: {}", e.getMessage(), e);
// 在异常情况下,设置默认结果
String errorMessage = String.format("导入失败: %s。成功 %d 条,失败 %d 条", e.getMessage(), result.getSuccessCount(), result.getFailureCount());
// 使用新方法确保类型正确
return ResponseVO.<ImportResult>fail(errorMessage, result);
}
@ -249,7 +233,6 @@ public class DeviceController {
try {
// 解码Base64字符串
byte[] data = Base64.getDecoder().decode(errorData);
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"import_errors.xlsx\"").contentType(MediaType.APPLICATION_OCTET_STREAM).body(data);
} catch (Exception e) {
log.error("下载错误报告失败", e);

View File

@ -81,4 +81,5 @@ public class DeviceTypeController {
return ResponseVO.success(deviceTypeService.getById(id));
}
}

View File

@ -271,8 +271,8 @@ public class UserController {
return ResponseVO.success(null);
}
@Log("修改用户:个人中心")
@ApiOperation("修改用户:个人中心")
@Log("修改用户:")
@ApiOperation("修改用户:")
@PutMapping(value = "center")
public ResponseVO<Object> centerUser(@Validated(User.Update.class) @RequestBody User resources) {
if (!resources.getId().equals(SecurityUtils.getCurrentUserId())) {

View File

@ -8,6 +8,7 @@ import com.fuyuanshen.modules.system.constant.UserConstants;
import com.fuyuanshen.modules.system.domain.Device;
import com.fuyuanshen.modules.system.domain.User;
import com.fuyuanshen.modules.system.domain.app.APPDevice;
import com.fuyuanshen.modules.system.domain.app.APPDeviceType;
import com.fuyuanshen.modules.system.domain.dto.CustomerVo;
import com.fuyuanshen.modules.system.domain.dto.DeviceExcelImportDTO;
import com.fuyuanshen.modules.system.domain.dto.DeviceForm;
@ -67,8 +68,39 @@ public class APPDeviceController {
private final APPDeviceService appDeviceService;
@PostMapping(value = "/list")
@ApiOperation("APP用户设备列表")
public ResponseVO<PageResult<APPDevice>> appDeviceList(@RequestBody DeviceQueryCriteria criteria) {
Page<APPDevice> page = new Page<>(criteria.getPage(), criteria.getSize());
PageResult<APPDevice> devices = null;
try {
devices = appDeviceService.appDeviceList(page, criteria);
} catch (Exception e) {
log.error("queryDevice error: " + e.getMessage());
return ResponseVO.fail("");
}
return ResponseVO.success(devices);
}
@PostMapping(value = "/typeList")
@ApiOperation("APP用户设备类型列表")
public ResponseVO<List<APPDeviceType>> appTypeList(@RequestBody DeviceQueryCriteria criteria) {
List<APPDeviceType> typeList = appDeviceService.appTypeList(criteria);
return ResponseVO.success(typeList);
}
@PostMapping(value = "/bind")
@ApiOperation("APP用户设备绑定")
public ResponseVO<String> appBindDevice(@RequestBody DeviceQueryCriteria criteria) {
appDeviceService.appBindDevice(criteria);
return ResponseVO.success("绑定成功!");
}
@GetMapping
@ApiOperation("查看APP用户设备绑定")
@ApiOperation("WEB端查看APP用户设备绑定")
public ResponseVO<PageResult<APPDevice>> queryAPPDevice(DeviceQueryCriteria criteria) {
Page<APPDevice> page = new Page<>(criteria.getPage(), criteria.getSize());
PageResult<APPDevice> devices = null;
@ -83,7 +115,7 @@ public class APPDeviceController {
@PostMapping(value = "/unbind")
@ApiOperation("APP用户设备解绑")
@ApiOperation("WEB端APP用户设备解绑")
public ResponseVO<String> unbindAPPDevice(@Validated @ModelAttribute APPUnbindDTO deviceForm) {
appDeviceService.unbindAPPDevice(deviceForm);
return ResponseVO.success("解绑成功!!!");

View File

@ -15,54 +15,30 @@
*/
package com.fuyuanshen.modules.system.rest.app;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fuyuanshen.annotation.Log;
import com.fuyuanshen.annotation.rest.AnonymousPostMapping;
import com.fuyuanshen.exception.BadRequestException;
import com.fuyuanshen.modules.security.service.UserCacheManager;
import com.fuyuanshen.modules.security.service.dto.JwtUserDto;
import com.fuyuanshen.modules.system.constant.UserConstants;
import com.fuyuanshen.modules.system.domain.Dept;
import com.fuyuanshen.modules.system.domain.Role;
import com.fuyuanshen.modules.system.domain.User;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import com.fuyuanshen.modules.system.domain.dto.UserPassVo;
import com.fuyuanshen.modules.system.domain.dto.DeviceForm;
import com.fuyuanshen.modules.system.domain.dto.UserQueryCriteria;
import com.fuyuanshen.modules.system.domain.dto.app.APPForgotPasswordDTO;
import com.fuyuanshen.modules.system.domain.dto.app.APPUpdateUserDTO;
import com.fuyuanshen.modules.system.domain.dto.app.APPUserDTO;
import com.fuyuanshen.modules.system.domain.query.APPUserQuery;
import com.fuyuanshen.modules.system.domain.vo.ConsumerVo;
import com.fuyuanshen.modules.system.domain.vo.APPUserVo;
import com.fuyuanshen.modules.system.enums.UserType;
import com.fuyuanshen.modules.system.mapper.UserMapper;
import com.fuyuanshen.modules.system.service.*;
import com.fuyuanshen.modules.system.service.app.APPUserService;
import com.fuyuanshen.modules.utils.ResponseVO;
import com.fuyuanshen.utils.PageResult;
import com.fuyuanshen.utils.SecurityUtils;
import com.fuyuanshen.utils.StringUtils;
import com.fuyuanshen.utils.enums.CodeEnum;
import io.netty.util.internal.StringUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.apache.ibatis.annotations.Param;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author Zheng Jie
@ -87,27 +63,75 @@ public class APPUserController {
return ResponseVO.success(appUserService.queryAPPUser(criteria, page));
}
@ApiOperation("用户中心")
@GetMapping(value = "/get")
@PreAuthorize("@el.check('appUser:get')")
public ResponseVO<APPUserVo> getAPPUser() {
String userName = SecurityUtils.getCurrentUsername();
APPUser appUser = appUserService.getLoginData(userName);
APPUserVo appUserVo = new APPUserVo();
appUserVo.setId(appUser.getId());
appUserVo.setNickName(appUser.getNickName());
appUserVo.setGender(appUser.getGender());
appUserVo.setPhone(appUser.getPhone());
return ResponseVO.success(appUserVo);
}
@Log("app用户注册")
@ApiOperation("app用户注册")
@AnonymousPostMapping(value = "/app/register")
@AnonymousPostMapping(value = "/register")
public ResponseVO<String> APPRegister(@Validated @RequestBody APPUserDTO user) throws Exception {
//暫定0000
if (user.getVerificationCode() == null || !"0000".equals(user.getVerificationCode())) {
if (!"0000".equals(user.getVerificationCode())) {
throw new BadRequestException("验证码错误");
}
appUserService.addUser(user);
return ResponseVO.success("success");
}
@Log("发送用户注册验证码")
@ApiOperation("发送用户注册验证码")
@AnonymousPostMapping(value = "/sendRegisterSms")
public ResponseVO<String> sendRegisterSms(@Param("phoneNumber") String phoneNumber) throws Exception {
// appUserService.sendSms(phoneNumber);
return ResponseVO.success("success");
}
@Log("修改APP用户")
@ApiOperation("修改APP用户")
@PutMapping
@PreAuthorize("@el.check('appUser:edit')")
public ResponseVO<String> updateUser(@Validated(APPUser.Update.class) @RequestBody APPUser appUser) throws Exception {
appUserService.updateById(appUser);
public ResponseVO<String> updateUser(@Validated @ModelAttribute APPUpdateUserDTO appUser) throws Exception {
Long userId = SecurityUtils.getCurrentUserId();
if(!userId.equals(appUser.getId())){
throw new BadRequestException("不能修改他人资料");
}
appUserService.updateUser(appUser);
return ResponseVO.success("success");
}
@Log("忘记密码")
@ApiOperation("忘记密码")
@AnonymousPostMapping(value = "/forgotPassword")
public ResponseVO<String> forgotPassword(@RequestBody APPForgotPasswordDTO appForgotPasswordDTO) throws Exception {
if (!"0000".equals(appForgotPasswordDTO.getVerificationCode())) {
throw new BadRequestException("验证码错误");
}
appUserService.forgotPassword(appForgotPasswordDTO);
return ResponseVO.success("success");
}
@Log("发送忘记密码验证码")
@ApiOperation("发送忘记密码验证码")
@AnonymousPostMapping(value = "/sendForgotPasswordSms")
public ResponseVO<String> sendForgotPasswordSms(@Param("phoneNumber") String phoneNumber) throws Exception {
// appUserService.sendSms(phoneNumber);
return ResponseVO.success("success");
}
}

View File

@ -80,6 +80,13 @@ public interface DeviceService extends IService<Device> {
*/
void deleteAll(List<Long> ids);
/**
* 删除设备分配记录
*
* @param ids
*/
void deleteAssign(List<Long> ids);
/**
* 导出数据
*
@ -103,4 +110,5 @@ public interface DeviceService extends IService<Device> {
*/
void unbindDevice(DeviceForm deviceForm);
}

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.fuyuanshen.modules.system.domain.Device;
import com.fuyuanshen.modules.system.domain.app.APPDevice;
import com.fuyuanshen.modules.system.domain.app.APPDeviceType;
import com.fuyuanshen.modules.system.domain.dto.CustomerVo;
import com.fuyuanshen.modules.system.domain.dto.DeviceForm;
import com.fuyuanshen.modules.system.domain.dto.DeviceQueryCriteria;
@ -22,6 +23,29 @@ import java.util.List;
**/
public interface APPDeviceService extends IService<APPDevice> {
/**
* APP用户设备列表
*
* @param criteria
*/
PageResult<APPDevice> appDeviceList(Page<APPDevice> page, DeviceQueryCriteria criteria);
/**
* APP用户设备类型列表
*
* @param criteria
* @return
*/
List<APPDeviceType> appTypeList(DeviceQueryCriteria criteria);
/**
* APP/小程序用户设备绑定
*
* @param criteria
*/
void appBindDevice(DeviceQueryCriteria criteria);
/**
* 分页查询APP/小程序设备绑定
*
@ -38,4 +62,5 @@ public interface APPDeviceService extends IService<APPDevice> {
*/
void unbindAPPDevice(APPUnbindDTO deviceForm);
}

View File

@ -2,48 +2,35 @@ package com.fuyuanshen.modules.system.service.app;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fuyuanshen.constants.DeviceConstants;
import com.fuyuanshen.constants.ExceptionMessages;
import com.fuyuanshen.exception.BadRequestException;
import com.fuyuanshen.modules.security.service.UserCacheManager;
import com.fuyuanshen.modules.system.constant.UserConstants;
import com.fuyuanshen.modules.system.domain.Device;
import com.fuyuanshen.modules.system.domain.DeviceType;
import com.fuyuanshen.modules.system.domain.User;
import com.fuyuanshen.modules.system.domain.app.APPDevice;
import com.fuyuanshen.modules.system.domain.dto.CustomerVo;
import com.fuyuanshen.modules.system.domain.dto.DeviceForm;
import com.fuyuanshen.modules.system.domain.app.APPDeviceType;
import com.fuyuanshen.modules.system.domain.dto.DeviceQueryCriteria;
import com.fuyuanshen.modules.system.domain.dto.app.APPUnbindDTO;
import com.fuyuanshen.modules.system.enums.BindingStatusEnum;
import com.fuyuanshen.modules.system.enums.CommunicationModeEnum;
import com.fuyuanshen.modules.system.enums.UserType;
import com.fuyuanshen.modules.system.mapper.DeviceMapper;
import com.fuyuanshen.modules.system.mapper.DeviceTypeMapper;
import com.fuyuanshen.modules.system.mapper.UserMapper;
import com.fuyuanshen.modules.system.mapper.app.APPDeviceMapper;
import com.fuyuanshen.modules.system.service.DeviceService;
import com.fuyuanshen.modules.system.service.UserService;
import com.fuyuanshen.modules.system.service.impl.DeviceTypeServiceImpl;
import com.fuyuanshen.modules.utils.NanoId;
import com.fuyuanshen.utils.*;
import com.fuyuanshen.modules.system.mapper.app.AppDeviceTypeMapper;
import com.fuyuanshen.utils.PageResult;
import com.fuyuanshen.utils.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.sql.Timestamp;
import java.util.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
@ -58,6 +45,89 @@ public class APPDeviceServiceImpl extends ServiceImpl<APPDeviceMapper, APPDevice
private final APPDeviceMapper appDeviceMapper;
private final DeviceMapper deviceMapper;
private final DeviceTypeMapper deviceTypeMapper;
private final AppDeviceTypeMapper appDeviceTypeMapper;
/**
* APP用户设备列表
*
* @param criteria
*/
@Override
public PageResult<APPDevice> appDeviceList(Page<APPDevice> page, DeviceQueryCriteria criteria) {
IPage<APPDevice> devices = appDeviceMapper.appDeviceList(page, criteria);
return new PageResult<>(devices.getRecords(), devices.getTotal());
}
/**
* APP用户设备类型列表
*
* @param criteria
* @return
*/
@Override
public List<APPDeviceType> appTypeList(DeviceQueryCriteria criteria) {
return appDeviceTypeMapper.appTypeList(criteria);
}
/**
* APP/小程序用户设备绑定
*
* @param criteria
*/
@Override
@Transactional
public void appBindDevice(DeviceQueryCriteria criteria) {
List<Device> devices = new ArrayList<>();
if (criteria.getCommunicationMode().equals(CommunicationModeEnum.BLUETOOTH.getValue())) {
devices = deviceMapper.selectList(new QueryWrapper<Device>().eq("device_mac", criteria.getDeviceMac()));
if (CollectionUtil.isEmpty(devices)) {
throw new BadRequestException("请先将设备入库!!!");
}
List<APPDevice> appDevices = appDeviceMapper.selectList(new QueryWrapper<APPDevice>()
.eq("device_mac", criteria.getDeviceMac()).eq("binding_type", UserType.APP.getValue()));
if (CollectionUtil.isNotEmpty(appDevices)) {
throw new BadRequestException("该设备已绑定!!!");
}
}
if (criteria.getCommunicationMode().equals(CommunicationModeEnum.FOUR_G.getValue())) {
devices = deviceMapper.selectList(new QueryWrapper<Device>().eq("device_imei", criteria.getDeviceImei()));
if (CollectionUtil.isEmpty(devices)) {
throw new BadRequestException("请先将设备入库!!!");
}
List<APPDevice> appDevices = appDeviceMapper.selectList(new QueryWrapper<APPDevice>()
.eq("device_imei", criteria.getDeviceImei()).eq("binding_type", UserType.APP.getValue()));
if (CollectionUtil.isNotEmpty(appDevices)) {
throw new BadRequestException("该设备已绑定!!!");
}
}
Device device = devices.get(0);
APPDevice appDevice = new APPDevice();
BeanUtil.copyProperties(device, appDevice);
appDevice.setBindingType(UserType.APP.getValue());
appDevice.setBindingStatus(BindingStatusEnum.BOUND.getCode());
Long currentUserId = SecurityUtils.getCurrentUserId();
appDevice.setCustomerId(currentUserId);
appDevice.setCreateTime(new Timestamp(System.currentTimeMillis()));
// 设备类型名称
appDevice.setDeviceTypeName(device.getTypeName());
appDeviceMapper.insert(appDevice);
APPDeviceType appDeviceType = appDeviceTypeMapper.selectById(device.getDeviceType());
if (appDeviceType == null) {
DeviceType deviceType = deviceTypeMapper.selectById(device.getDeviceType());
APPDeviceType type = new APPDeviceType();
BeanUtil.copyProperties(deviceType, type);
appDeviceTypeMapper.insert(type);
}
}
/**
@ -83,11 +153,17 @@ public class APPDeviceServiceImpl extends ServiceImpl<APPDeviceMapper, APPDevice
@Transactional
public void unbindAPPDevice(APPUnbindDTO deviceForm) {
appDeviceMapper.delete(new QueryWrapper<APPDevice>().eq("device_mac", deviceForm.getDeviceMac()));
appDeviceMapper.delete(new QueryWrapper<APPDevice>().eq("device_mac", deviceForm.getDeviceMac()).eq("binding_type", UserType.APP.getValue()));
List<Device> devices = deviceMapper.selectList(new QueryWrapper<Device>().eq("device_mac", deviceForm.getDeviceMac()));
List<Long> ids = devices.stream()
.map(Device::getId)
.collect(Collectors.toList());
appDeviceTypeMapper.deleteBatchIds(ids);
Device device = new Device();
device.setId(deviceForm.getCustomerId());
device.setBindingStatus(BindingStatusEnum.UNBOUND.getCode());
deviceMapper.updateById(device);
}
}

View File

@ -21,6 +21,8 @@ import com.fuyuanshen.modules.security.service.dto.app.AppRoleDto;
import com.fuyuanshen.modules.system.domain.Role;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import com.fuyuanshen.modules.system.domain.dto.UserQueryCriteria;
import com.fuyuanshen.modules.system.domain.dto.app.APPForgotPasswordDTO;
import com.fuyuanshen.modules.system.domain.dto.app.APPUpdateUserDTO;
import com.fuyuanshen.modules.system.domain.dto.app.APPUserDTO;
import com.fuyuanshen.modules.utils.ResponseVO;
import com.fuyuanshen.utils.PageResult;
@ -54,4 +56,10 @@ public interface APPUserService extends IService<APPUser> {
ResponseVO<Object> addUser(APPUserDTO user);
Integer selectRoleByUserLevel(Set<Role> roles);
void forgotPassword(APPForgotPasswordDTO appForgotPasswordDTO);
void sendSms(String phoneNumber);
void updateUser(APPUpdateUserDTO appUser);
}

View File

@ -0,0 +1,13 @@
package com.fuyuanshen.modules.system.service.app;
import com.baomidou.mybatisplus.extension.service.IService;
import com.fuyuanshen.modules.system.domain.app.APPDeviceType;
/**
* @author 97433
* @description 针对表【app_device_type(设备类型表)】的数据库操作Service
* @createDate 2025-06-24 11:16:18
*/
public interface AppDeviceTypeService extends IService<APPDeviceType> {
}

View File

@ -41,7 +41,8 @@ public class DeviceExportService {
dto.setCustomerName(device.getCustomerName());
dto.setDeviceName(device.getDeviceName());
dto.setDeviceMac(device.getDeviceMac());
dto.setDeviceSn(device.getDeviceSn());
// 设备IMEI
dto.setDeviceImei(device.getDeviceImei());
dto.setLongitude(device.getLongitude());
dto.setLatitude(device.getLatitude());
dto.setRemark(device.getRemark());

View File

@ -2,7 +2,7 @@ package com.fuyuanshen.modules.system.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -18,19 +18,19 @@ import com.fuyuanshen.modules.system.domain.dto.CustomerVo;
import com.fuyuanshen.modules.system.domain.dto.DeviceForm;
import com.fuyuanshen.modules.system.domain.dto.DeviceQueryCriteria;
import com.fuyuanshen.modules.system.enums.BindingStatusEnum;
import com.fuyuanshen.modules.system.enums.DeviceActiveStatusEnum;
import com.fuyuanshen.modules.system.enums.DeviceAuthorizationStatus;
import com.fuyuanshen.modules.system.enums.DeviceStatusEnum;
import com.fuyuanshen.modules.system.mapper.DeviceMapper;
import com.fuyuanshen.modules.system.mapper.DeviceTypeGrantsMapper;
import com.fuyuanshen.modules.system.mapper.DeviceTypeMapper;
import com.fuyuanshen.modules.system.mapper.UserMapper;
import com.fuyuanshen.modules.system.mapper.*;
import com.fuyuanshen.modules.system.mapper.app.APPDeviceMapper;
import com.fuyuanshen.modules.system.service.DeviceAssignmentsService;
import com.fuyuanshen.modules.system.service.DeviceService;
import com.fuyuanshen.modules.system.service.DeviceTypeGrantsService;
import com.fuyuanshen.modules.system.service.UserService;
import com.fuyuanshen.modules.utils.AdminCheckUtil;
import com.fuyuanshen.modules.utils.NanoId;
import com.fuyuanshen.utils.*;
import com.fuyuanshen.utils.enums.NanoIdLengthEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@ -42,6 +42,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
@ -55,6 +56,12 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements DeviceService {
/**
* 初级分配等级
*/
public static final int LEVEL_PRIMARY = 1;
@Value("${file.device.pic}")
private String filePath;
@Value("${file.device.ip}")
@ -70,6 +77,7 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
private final APPDeviceMapper appDeviceMapper;
private final DeviceTypeGrantsMapper deviceTypeGrantsMapper;
private final DeviceAssignmentsService deviceAssignmentsService;
private final DeviceAssignmentsMapper deviceAssignmentsMapper;
private final DeviceTypeGrantsService deviceTypeGrantsService;
@Autowired
@ -182,11 +190,27 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
device.setDeviceStatus(DeviceStatusEnum.NORMAL.getCode());
// SnowflakeGenerator snowflakeGenerator = new SnowflakeGenerator();
// device.setId(snowflakeGenerator.next());
device.setCurrentOwnerId(currentUser.getId());
device.setOriginalOwnerId(currentUser.getId());
DeviceType deviceType = deviceTypeMapper.selectById(deviceForm.getDeviceType());
device.setTypeName(deviceType.getTypeName());
deviceMapper.insert(device);
// 新增设备类型记录
DeviceAssignments assignments = new DeviceAssignments();
assignments.setDeviceId(device.getId());
assignments.setAssignedAt(LocalDateTime.now());
// 分配者
assignments.setAssignerId(currentUser.getId());
assignments.setAssignerName(currentUser.getUsername());
// 接收者
assignments.setAssigneeId(currentUser.getId());
assignments.setActive(DeviceActiveStatusEnum.ACTIVE.getCode());
String lever = currentUser.getId() + ":";
assignments.setLever(lever);
deviceAssignmentsService.save(assignments);
}
@ -242,13 +266,12 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
/**
* 分配客户
* 分配客户 历史记录版本
*
* @param customerVo
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void assignCustomer(CustomerVo customerVo) {
public void assignCustomer1(CustomerVo customerVo) {
// 防止管理员误操作
User currentUser = userService.findById(SecurityUtils.getCurrentUserId());
@ -268,7 +291,7 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
}
// 自定义16位id
Long generatedId = NanoId.generate(16);
Long generatedId = NanoId.generate(NanoIdLengthEnum.HIGH_CONCURRENCY.getLength());
// -- 记录分配历史
DeviceAssignments deviceAssignments = new DeviceAssignments();
@ -276,7 +299,7 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
deviceAssignments.setDeviceId(deviceId);
deviceAssignments.setFromCustomerId(currentUser.getId());
deviceAssignments.setToCustomerId(customerVo.getCustomerId());
deviceAssignments.setAssignedAt(new Date());
deviceAssignments.setAssignedAt(LocalDateTime.now());
deviceAssignments.setDeviceTypeGranted(DeviceAuthorizationStatus.AUTHORIZED.getValue());
assignments.add(deviceAssignments);
@ -296,7 +319,7 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
// -- 更新设备所有者
device.setCurrentOwnerId(customerVo.getCustomerId());
devices.add( device);
devices.add(device);
});
deviceAssignmentsService.saveBatch(assignments);
@ -307,79 +330,152 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
/**
* 分配客户代码复制
* 分配客户
*
* @param customerVo
*/
public void assignCustomer1(CustomerVo customerVo) {
@Override
@Transactional(rollbackFor = Exception.class)
public void assignCustomer(CustomerVo customerVo) {
// 防止管理员误操作
// 获取当前登录用户
User currentUser = userService.findById(SecurityUtils.getCurrentUserId());
if (currentUser.getTenantId().equals(UserConstants.SUPER_ADMIN_ID)) {
throw new BadRequestException(ExceptionMessages.ADMIN_OPERATION_NOT_ALLOWED);
}
// 防止管理员误操作
AdminCheckUtil.checkIfSuperAdmin(currentUser);
// 获取分配用户信息
User assignUser = userService.findById(customerVo.getCustomerId());
User user = userService.findById(customerVo.getCustomerId());
// 获取分配设备信息
List<Device> devices = deviceMapper.selectBatchIds(customerVo.getDeviceIds());
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
for (Device device : devices) {
device.setCustomerId(user.getId());
device.setCustomerName(user.getNickName());
device.setUpdateTime(timestamp);
}
this.updateBatchById(devices);
// 批量更新设备状态
List<DeviceTypeGrants> deviceTypeGrants = new ArrayList<>();
for (Device device : devices) {
// 获取当前用户的所有祖先用户
List<User> ancestorsById = userMapper.findAncestorsById(currentUser.getId());
Set<Long> excludedTenantIds = new HashSet<>();
if (CollectionUtil.isNotEmpty(ancestorsById)) {
// 提取所有需要排除的 tenant_id
excludedTenantIds = ancestorsById.stream().map(User::getTenantId).distinct().collect(Collectors.toSet());
}
excludedTenantIds.add(currentUser.getTenantId());
// 构建查询条件device_mac 相同,并且 tenant_id 不在 excludedTenantIds 列表中
Wrapper<Device> wrapper = new QueryWrapper<Device>().eq("device_mac", device.getDeviceMac()).notIn("tenant_id", excludedTenantIds);
// 构建要更新的数据
Device updateDevice = new Device();
updateDevice.setDeviceStatus(0);
updateDevice.setUpdateTime(new Timestamp(System.currentTimeMillis()));
// 如果设备已分配给需要分配的客户,则跳过
LambdaQueryWrapper<DeviceAssignments> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(DeviceAssignments::getDeviceId, device.getId()).eq(DeviceAssignments::getAssigneeId, customerVo.getCustomerId()).ne(DeviceAssignments::getActive, DeviceActiveStatusEnum.INACTIVE.getCode());
List<DeviceAssignments> deviceAssignments = deviceAssignmentsMapper.selectList(wrapper);
if (CollectionUtil.isNotEmpty(deviceAssignments)) {
log.info("设备 {} 已分配给客户 {}", device.getDeviceName(), device.getCustomerName());
continue;
}
// 更改分配客户
QueryWrapper<DeviceAssignments> q = new QueryWrapper<>();
q.eq("device_id", device.getId());
q.eq("assignee_id", currentUser.getId());
q.ne("active", DeviceActiveStatusEnum.INACTIVE.getCode());
DeviceAssignments d = new DeviceAssignments();
d.setAssigneeName(assignUser.getUsername());
deviceAssignmentsMapper.update(d, q);
// 设备失效
// 获取用户的设备记录
DeviceAssignments assignment = deviceAssignmentsMapper.selectOne(new LambdaQueryWrapper<DeviceAssignments>().eq(DeviceAssignments::getDeviceId, device.getId()).eq(DeviceAssignments::getAssigneeId, currentUser.getId()).eq(DeviceAssignments::getActive, DeviceActiveStatusEnum.ACTIVE.getCode()));
LambdaQueryWrapper<DeviceAssignments> q1 = new LambdaQueryWrapper<>();
q1.eq(DeviceAssignments::getDeviceId, device.getId()).ne(DeviceAssignments::getAssigneeId, currentUser.getId()).like(DeviceAssignments::getLever, assignment.getLever());
DeviceAssignments d1 = new DeviceAssignments();
d1.setActive(DeviceActiveStatusEnum.INACTIVE.getCode());
deviceAssignmentsMapper.update(d1, q1);
// device.setCustomerId(assignUser.getId());
// device.setCustomerName(assignUser.getUsername());
// 新增设备类型记录
DeviceAssignments assignments = new DeviceAssignments();
assignments.setDeviceId(device.getId());
assignments.setAssignedAt(LocalDateTime.now());
// 分配者
assignments.setAssignerId(currentUser.getId());
assignments.setAssignerName(currentUser.getUsername());
// 接收者
assignments.setAssigneeId(assignUser.getId());
// assignments.setAssigneeName(assignUser.getUsername());
assignments.setActive(DeviceActiveStatusEnum.ACTIVE.getCode());
String lever = assignment.getLever() + ":" + assignUser.getId();
assignments.setLever(lever);
deviceAssignmentsService.save(assignments);
// // 获取当前用户的所有祖先用户
// List<User> ancestorsById = userMapper.findAncestorsById(currentUser.getId());
// Set<Long> excludedTenantIds = new HashSet<>();
// if (CollectionUtil.isNotEmpty(ancestorsById)) {
// // 提取所有需要排除的 tenant_id
// excludedTenantIds = ancestorsById.stream().map(User::getTenantId).distinct().collect(Collectors.toSet());
// }
// excludedTenantIds.add(currentUser.getTenantId());
// // 构建查询条件device_mac 相同,并且 tenant_id 不在 excludedTenantIds 列表中
// Wrapper<Device> wrapper = new QueryWrapper<Device>().eq("device_mac", device.getDeviceMac()).notIn("tenant_id", excludedTenantIds);
//
// // 构建要更新的数据
// Device updateDevice = new Device();
// updateDevice.setDeviceStatus(0);
// updateDevice.setUpdateTime(new Timestamp(System.currentTimeMillis()));
// 根据条件批量更新
deviceMapper.update(updateDevice, wrapper);
// deviceMapper.update(updateDevice, wrapper);
// 创建并保存设备类型授权记录
createAndSaveDeviceTypeGrants(device, currentUser, customerVo, deviceTypeGrants);
}
// 批量分配
Set<DeviceType> deviceTypes = new HashSet<>();
for (Device device : devices) {
device.setId(null);
device.setCustomerName(null);
device.setCustomerId(null);
device.setTenantId(user.getTenantId());
device.setCreateTime(timestamp);
device.setCreateBy(currentUser.getUsername());
device.setUpdateTime(timestamp);
this.updateBatchById(devices);
// 查询设备类型
DeviceType deviceType = deviceTypeMapper.selectById(device.getDeviceType());
// SnowflakeGenerator snowflakeGenerator = new SnowflakeGenerator();
Long id = NanoId.generate(16);
deviceType.setId(id);
deviceType.setCreateTime(timestamp);
deviceType.setCreateBy(currentUser.getUsername());
deviceType.setCustomerId(customerVo.getCustomerId());
deviceTypes.add(deviceType);
deviceTypeGrantsService.saveBatch(deviceTypeGrants);
device.setDeviceType(deviceType.getId());
}
this.saveBatch(devices);
deviceTypeService.saveBatch(deviceTypes);
//
// // 批量分配
// Set<DeviceType> deviceTypes = new HashSet<>();
// for (Device device : devices) {
// device.setId(null);
// device.setCustomerName(null);
// device.setCustomerId(null);
// device.setTenantId(user.getTenantId());
// device.setCreateTime(timestamp);
// device.setCreateBy(currentUser.getUsername());
// device.setUpdateTime(timestamp);
//
// // 查询设备类型
// DeviceType deviceType = deviceTypeMapper.selectById(device.getDeviceType());
// // SnowflakeGenerator snowflakeGenerator = new SnowflakeGenerator();
// Long id = NanoId.generate(NanoIdLengthEnum.HIGH_CONCURRENCY.getLength());
// deviceType.setId(id);
// deviceType.setCreateTime(timestamp);
// deviceType.setCreateBy(currentUser.getUsername());
// deviceType.setCustomerId(customerVo.getCustomerId());
// deviceTypes.add(deviceType);
//
// device.setDeviceType(deviceType.getId());
// }
// this.saveBatch(devices);
// deviceTypeService.saveBatch(deviceTypes);
}
/**
* 创建并保存设备类型授权记录
*
* @param device 当前设备对象
* @param currentUser 当前登录用户
* @param customerVo 客户信息
* @param deviceTypeGrants 授权记录集合
*/
private void createAndSaveDeviceTypeGrants(Device device, User currentUser, CustomerVo customerVo, List<DeviceTypeGrants> deviceTypeGrants) {
Long generatedId = NanoId.generate(NanoIdLengthEnum.HIGH_CONCURRENCY.getLength());
DeviceTypeGrants deviceTypeGrant = new DeviceTypeGrants();
deviceTypeGrant.setGrantedAt(new Date());
deviceTypeGrant.setDeviceTypeId(device.getDeviceType());
deviceTypeGrant.setAssignmentId(generatedId);
deviceTypeGrant.setCustomerId(customerVo.getCustomerId());
deviceTypeGrant.setGrantorCustomerId(currentUser.getId());
deviceTypeGrants.add(deviceTypeGrant);
}
/**
* 删除设备
*
@ -389,18 +485,17 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
@Transactional(rollbackFor = Exception.class)
public void deleteAll(List<Long> ids) {
SecurityUtils.getCurrentUserId();
// Step 1: 查询所有传入的设备(根据 ID
List<Device> allDevices = deviceMapper.selectBatchIds(ids);
// Step 2: 使用 Java Stream 过滤出 customer_id 不为 null 的设备
Set<Long> nonNullCustomerIds = allDevices.stream().filter(device -> device.getCustomerId() != null && device.getDeviceStatus() == 1).map(Device::getId).collect(Collectors.toSet());
// Step 3: 从原始 ids 中“去掉”这些非空 customer_id 的设备 ID
List<Long> remainingIds = ids.stream().filter(id -> !nonNullCustomerIds.contains(id)).collect(Collectors.toList());
if (CollectionUtil.isEmpty(remainingIds)) {
throw new BadRequestException("已分配的设备不允许删除!!!");
}
List<Device> devices = deviceMapper.selectBatchIds(remainingIds);
for (Device device : devices) {
String devicePic = device.getDevicePic();
@ -419,6 +514,58 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
deviceMapper.deleteBatchIds(ids);
}
/**
* 删除设备分配记录分配记录id
*
* @param ids
*/
public void deleteAssign1(List<Long> ids) {
Long currentUserId = SecurityUtils.getCurrentUserId();
// Step 1: 查询所有传入的设备(根据 ID
// List<DeviceAssignments> deviceAssignments = deviceAssignmentsMapper.selectBatchIds(ids);
QueryWrapper<DeviceAssignments> wrapper = new QueryWrapper<>();
// wrapper.eq("active", DeviceActiveStatusEnum.INACTIVE.getCode());
wrapper.in("device_id", ids);
wrapper.eq("assigner_id", currentUserId);
List<DeviceAssignments> deviceAssignments = deviceAssignmentsMapper.selectList(wrapper);
// Step 2: 使用 Java Stream 过滤出 customer_id 不为 null 的设备
Set<Long> nonNullCustomerIds = deviceAssignments.stream().filter(device -> !StringUtils.isNotEmpty(device.getAssigneeName())).map(DeviceAssignments::getId).collect(Collectors.toSet());
if (CollectionUtil.isEmpty(nonNullCustomerIds)) {
throw new BadRequestException("已分配的设备不允许删除!!!");
}
// QueryWrapper<DeviceAssignments> de = new QueryWrapper<>();
// wrapper.eq("active", DeviceActiveStatusEnum.INACTIVE.getCode());
// wrapper.in("device_id", ids);
// wrapper.eq("assignee_id", currentUserId);
// deviceAssignmentsMapper.delete(de);
deviceAssignmentsMapper.deleteBatchIds(nonNullCustomerIds);
}
/**
* 删除设备分配记录分配记录id
*
* @param ids
*/
@Override
public void deleteAssign(List<Long> ids) {
// Step 1: 查询所有传入的设备(根据 ID
List<DeviceAssignments> deviceAssignments = deviceAssignmentsMapper.selectBatchIds(ids);
// Step 2: 使用 Java Stream 过滤出 customer_id 不为 null 的设备
Set<Long> nonNullCustomerIds = deviceAssignments.stream()
.filter(device -> !StringUtils.isNotEmpty(device.getAssigneeName()))
.map(DeviceAssignments::getId).collect(Collectors.toSet());
if (CollectionUtil.isEmpty(nonNullCustomerIds)) {
throw new BadRequestException("已分配的设备不允许删除!!!");
}
deviceAssignmentsMapper.deleteBatchIds(ids);
}
@Override
public void download(List<Device> all, HttpServletResponse response) throws IOException {
List<Map<String, Object>> list = new ArrayList<>();

View File

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fuyuanshen.modules.system.domain.DeviceTypeGrants;
import com.fuyuanshen.modules.system.mapper.DeviceTypeGrantsMapper;
import com.fuyuanshen.modules.utils.NanoId;
import com.fuyuanshen.utils.enums.NanoIdLengthEnum;
import lombok.RequiredArgsConstructor;
import com.fuyuanshen.modules.security.service.UserCacheManager;
import com.fuyuanshen.modules.security.service.dto.JwtUserDto;
@ -125,9 +126,10 @@ public class DeviceTypeServiceImpl extends ServiceImpl<DeviceTypeMapper, DeviceT
@Override
@Transactional(rollbackFor = Exception.class)
public void create(DeviceType resources) {
Long deviceTypeId = NanoId.generate(16);
Long deviceTypeId = NanoId.generate(NanoIdLengthEnum.HIGH_CONCURRENCY.getLength());
resources.setId(deviceTypeId);
resources.setCustomerId(SecurityUtils.getCurrentUserId());
resources.setOwnerCustomerId(SecurityUtils.getCurrentUserId());
deviceTypeMapper.insert(resources);
// 自动授权给自己
@ -137,7 +139,6 @@ public class DeviceTypeServiceImpl extends ServiceImpl<DeviceTypeMapper, DeviceT
deviceTypeGrants.setGrantorCustomerId(SecurityUtils.getCurrentUserId());
deviceTypeGrants.setGrantedAt(new Date());
deviceTypeGrantsMapper.insert(deviceTypeGrants);
}

View File

@ -19,7 +19,10 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fuyuanshen.modules.system.domain.app.APPMenu;
import com.fuyuanshen.modules.system.domain.app.APPRole;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import com.fuyuanshen.modules.system.mapper.*;
import lombok.RequiredArgsConstructor;
import com.fuyuanshen.exception.BadRequestException;
import com.fuyuanshen.modules.security.service.UserCacheManager;
@ -28,10 +31,6 @@ import com.fuyuanshen.modules.system.domain.Menu;
import com.fuyuanshen.modules.system.domain.Role;
import com.fuyuanshen.exception.EntityExistException;
import com.fuyuanshen.modules.system.domain.User;
import com.fuyuanshen.modules.system.mapper.RoleDeptMapper;
import com.fuyuanshen.modules.system.mapper.RoleMapper;
import com.fuyuanshen.modules.system.mapper.RoleMenuMapper;
import com.fuyuanshen.modules.system.mapper.UserMapper;
import com.fuyuanshen.modules.system.service.RoleService;
import com.fuyuanshen.modules.system.domain.dto.RoleQueryCriteria;
import com.fuyuanshen.utils.*;
@ -53,6 +52,7 @@ import java.util.stream.Collectors;
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {
private final RoleMapper roleMapper;
private final APPRoleMapper appRoleMapper;
private final RoleDeptMapper roleDeptMapper;
private final RoleMenuMapper roleMenuMapper;
private final RedisUtils redisUtils;
@ -206,8 +206,8 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements Ro
permissions.add("admin");
return permissions.stream().map(AuthorityDto::new).collect(Collectors.toList());
}
List<Role> roles = roleMapper.findByUserId(user.getId());
permissions = roles.stream().flatMap(role -> role.getMenus().stream()).map(Menu::getPermission).filter(StringUtils::isNotBlank).collect(Collectors.toSet());
List<APPRole> roles = appRoleMapper.findByUserId(user.getId());
permissions = roles.stream().flatMap(role -> role.getMenus().stream()).map(APPMenu::getPermission).filter(StringUtils::isNotBlank).collect(Collectors.toSet());
authorityDtos = permissions.stream().map(AuthorityDto::new).collect(Collectors.toList());
redisUtils.set(key, authorityDtos, 1, TimeUnit.HOURS);
}

View File

@ -15,22 +15,35 @@
*/
package com.fuyuanshen.modules.system.service.impl.app;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.MD5;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fuyuanshen.constants.DeviceConstants;
import com.fuyuanshen.exception.BadRequestException;
import com.fuyuanshen.modules.system.domain.Role;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import com.fuyuanshen.modules.system.domain.dto.UserQueryCriteria;
import com.fuyuanshen.modules.system.domain.dto.app.APPForgotPasswordDTO;
import com.fuyuanshen.modules.system.domain.dto.app.APPUpdateUserDTO;
import com.fuyuanshen.modules.system.domain.dto.app.APPUserDTO;
import com.fuyuanshen.modules.system.mapper.app.APPUserMapper;
import com.fuyuanshen.modules.system.service.app.APPUserService;
import com.fuyuanshen.modules.utils.ResponseVO;
import com.fuyuanshen.utils.*;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.Set;
import static com.fuyuanshen.constants.RedisConstants.*;
/**
* @author Zheng Jie
* @date 2018-11-23
@ -41,8 +54,21 @@ public class APPUserServiceImpl extends ServiceImpl<APPUserMapper, APPUser> impl
private final APPUserMapper appUserMapper;
private final PasswordEncoder passwordEncoder;
@Autowired
private final RedisUtils redisUtils;
@Value("${file.app_avatar.pic}")
private String filePath;
@Value("${file.app_avatar.ip}")
private String ip;
/**
* 文件访问路径前缀
*/
public static final String FILE_ACCESS_PREFIX = "images";
public static final String FILE_AVATAR_PREFIX = "avatar";
/**
* 查询APP/小程序用户
*
@ -67,24 +93,33 @@ public class APPUserServiceImpl extends ServiceImpl<APPUserMapper, APPUser> impl
@Override
public ResponseVO<Object> addUser(APPUserDTO user) {
String username = user.getPhoneNumber().toString();
String username = user.getPhoneNumber();
if (appUserMapper.getByUsername(username) != null) {
throw new BadRequestException("该手机号已被注册");
}
APPUser appUser = new APPUser();
appUser.setUsername(user.getPhoneNumber().toString());
appUser.setPassword(user.getPassword());
appUser.setNickName(user.getPhoneNumber());
appUser.setUserLevel((byte) 1);
appUser.setPhone(Long.valueOf(user.getPhoneNumber()));
appUser.setAdmin((byte) 1);
appUser.setEnabled(true);
appUser.setUserType(0);
System.out.println("--------------------"+appUser);
/* Object verificationCode = redisUtils.get(APP_REGISTER_SMS_TOKEN + username);
if (verificationCode == null) {
throw new BadRequestException("验证码已过期");
}
if(!user.getVerificationCode().equals(verificationCode.toString())){
throw new BadRequestException("验证码错误");
}*/
APPUser appUser = new APPUser();
appUser.setUsername(user.getPhoneNumber());
appUser.setPassword(user.getPassword());
appUser.setNickName(user.getPhoneNumber());
appUser.setUserLevel((byte) 1);
appUser.setPhone(Long.valueOf(user.getPhoneNumber()));
appUser.setAdmin((byte) 1);
appUser.setEnabled(true);
appUser.setUserType(0);
System.out.println("--------------------"+appUser);
appUserMapper.insert(appUser);
return ResponseVO.success("注册成功");
appUserMapper.insert(appUser);
return ResponseVO.success("注册成功");
}
@ -93,4 +128,67 @@ public class APPUserServiceImpl extends ServiceImpl<APPUserMapper, APPUser> impl
return 0;
}
@Override
public void forgotPassword(APPForgotPasswordDTO appForgotPasswordDTO) {
String phoneNumber = appForgotPasswordDTO.getPhoneNumber();
APPUser appUser = appUserMapper.appFindByUsername(phoneNumber);
if (appUser == null) {
throw new BadRequestException("手机号不存在");
}
/*Object verificationCode = redisUtils.get(APP_FORGOT_PASSWORD_SMS_TOKEN + phoneNumber);
if (verificationCode == null) {
throw new BadRequestException("验证码已过期");
}
if(!appForgotPasswordDTO.getVerificationCode().equals(verificationCode.toString())){
throw new BadRequestException("验证码错误");
}*/
appUser.setPassword(appForgotPasswordDTO.getPassword());
appUserMapper.updateById(appUser);
}
@Override
public void sendSms(String phoneNumber) {
if (appUserMapper.appFindByUsername(phoneNumber) == null) {
throw new BadRequestException("手机号不存在");
}
// todo 发送验证码
redisUtils.set(APP_FORGOT_PASSWORD_SMS_TOKEN + phoneNumber, RandomUtil.randomNumbers(4), 5 * 60);
}
@Override
public void updateUser(APPUpdateUserDTO appUser) {
APPUser updUser= new APPUser();
updUser.setId(appUser.getId());
updUser.setNickName(appUser.getNickName());
try {
updUser.setAvatarPath(saveUserAvatarImage(appUser.getFile()));
}catch (IOException e){
throw new BadRequestException("上传头像失败");
}
updUser.setRegion(appUser.getRegion());
updUser.setGender(appUser.getGender());
appUserMapper.updateById(updUser);
}
private String saveUserAvatarImage(MultipartFile file) throws IOException {
if (file == null || file.isEmpty()) {
return null;
}
String originalFileName = file.getOriginalFilename();
String fileExtension = originalFileName.substring(originalFileName.lastIndexOf(".") + 1);
String newFileName = "PS_" + RandomUtil.randomNumbers(8) + "." + fileExtension;
File newFile = new File(filePath + FILE_AVATAR_PREFIX + File.separator + newFileName);
if (!newFile.getParentFile().exists()) {
newFile.getParentFile().mkdirs();
}
file.transferTo(newFile);
return ip + DeviceConstants.FILE_ACCESS_PREFIX + "/" + FILE_AVATAR_PREFIX + "/" + newFileName;
}
}

View File

@ -0,0 +1,18 @@
package com.fuyuanshen.modules.system.service.impl.app;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fuyuanshen.modules.system.domain.app.APPDeviceType;
import com.fuyuanshen.modules.system.mapper.app.AppDeviceTypeMapper;
import com.fuyuanshen.modules.system.service.app.AppDeviceTypeService;
import org.springframework.stereotype.Service;
/**
* @author 97433
* @description 针对表【app_device_type(设备类型表)】的数据库操作Service实现
* @createDate 2025-06-24 11:16:18
*/
@Service
public class AppDeviceTypeServiceImpl extends ServiceImpl<AppDeviceTypeMapper, APPDeviceType>
implements AppDeviceTypeService {
}

View File

@ -0,0 +1,40 @@
package com.fuyuanshen.modules.utils;
import com.fuyuanshen.constants.ExceptionMessages;
import com.fuyuanshen.exception.BadRequestException;
import com.fuyuanshen.modules.system.constant.UserConstants;
import com.fuyuanshen.modules.system.domain.User;
import com.fuyuanshen.modules.system.service.UserService;
import com.fuyuanshen.utils.SecurityUtils;
/**
* @author: 默苍璃
* @date: 2025-06-2110:35
*/
public class AdminCheckUtil {
/**
* 检查当前用户是否为超级管理员,如果是则抛出异常,防止误操作
*
* @param userService UserService 用于获取当前用户信息
*/
public static void preventAdminMisoperation(UserService userService) {
User currentUser = userService.findById(SecurityUtils.getCurrentUserId());
if (currentUser.getTenantId().equals(UserConstants.SUPER_ADMIN_ID)) {
throw new BadRequestException(ExceptionMessages.ADMIN_OPERATION_NOT_ALLOWED);
}
}
/**
* 检查用户是否是超级管理员,如果是则抛出异常
*
* @param user 当前用户对象
*/
public static void checkIfSuperAdmin(User user) {
if (user.getTenantId().equals(UserConstants.SUPER_ADMIN_ID)) {
throw new BadRequestException(ExceptionMessages.ADMIN_OPERATION_NOT_ALLOWED);
}
}
}

View File

@ -4,9 +4,9 @@ spring:
druid:
db-type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://192.168.2.23:3306/eladmin?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
url: jdbc:p6spy:mysql://120.79.224.186:3366/eladmin?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
username: root
password: root
password: 1fys@QWER..
# 初始连接数,建议设置为与最小空闲连接数相同
initial-size: 20
# 最小空闲连接数,保持足够的空闲连接以应对请求
@ -53,10 +53,10 @@ spring:
redis:
#数据库索引
database: ${REDIS_DB:2}
host: ${REDIS_HOST:123.207.99.140}
port: ${REDIS_PORT:6379}
password: ${REDIS_PWD:ccxx11234}
database: ${REDIS_DB:0}
host: ${REDIS_HOST:120.79.224.186}
port: ${REDIS_PORT:26379}
password: ${REDIS_PWD:1fys@QWER..}
#连接超时时间
timeout: 5000
# 连接池配置
@ -107,6 +107,8 @@ jwt:
token-validity-in-seconds: 14400000
# 在线用户key
online-key: "online_token:"
# app在线用户key
app_online-key: "app_online_token:"
# 验证码
code-key: "captcha_code:"
# token 续期检查时间范围默认30分钟单位毫秒在token即将过期的一段时间内用户操作了则给用户的token续期
@ -139,7 +141,20 @@ file:
device:
pic: C:\eladmin\file\ #设备图片存储路径
ip: http://fuyuanshen.com:81/ #服务器地址
app_avatar:
pic: C:\eladmin\file\ #设备图片存储路径
#ip: http://fuyuanshen.com:81/ #服务器地址
ip: https://fuyuanshen.com/ #服务器地址
logging:
level:
com.fuyuanshen: debug
# MQTT配置
mqtt:
username: admin
password: fys123456
url: tcp://127.0.0.1:1883
subClientId: wuLang_subClient_01
subTopic: worker/alert/#,worker/location/#
pubTopic: worker/location
pubClientId: wuLang_pubClient_01

View File

@ -151,3 +151,7 @@ file:
pic: /home/eladmin/file/ #设备图片存储路径
#ip: http://fuyuanshen.com:81/ #服务器地址
ip: https://fuyuanshen.com/ #服务器地址
app_avatar:
pic: /home/eladmin/app_avatar/ #设备图片存储路径
#ip: http://fuyuanshen.com:81/ #服务器地址
ip: https://fuyuanshen.com/ #服务器地址

View File

@ -43,6 +43,8 @@ spring:
multipart:
max-file-size: 5MB # 设置单个上传文件的最大大小为10MB
max-request-size: 5MB
jackson:
default-property-inclusion: non_null
# pid:
# file: /自行指定位置/eladmin.pid

View File

@ -19,17 +19,20 @@
<result column="binding_status" property="bindingStatus"/>
</resultMap>
<!-- 分页查询APP/小程序设备 -->
<select id="queryAll" resultType="com.fuyuanshen.modules.system.domain.app.APPDevice">
<!-- APP用户设备列表 -->
<select id="appDeviceList" resultType="com.fuyuanshen.modules.system.domain.app.APPDevice">
select d.* from app_device as d
<where>
<!-- 时间范围等其他条件保持原样 -->
<if test="criteria.deviceName != null and criteria.deviceName.trim() != ''">
and d.device_name like concat('%', TRIM(#{criteria.deviceName}), '%')
</if>
<if test="criteria.deviceMac != null and criteria.deviceName.trim() != ''">
<if test="criteria.deviceMac != null and criteria.deviceMac.trim() != ''">
and d.device_mac = #{criteria.deviceMac}
</if>
<if test="criteria.deviceImei != null and criteria.deviceImei.trim() != ''">
and d.device_imei = #{criteria.deviceImei}
</if>
<if test="criteria.deviceSn != null">
and d.device_sn = #{criteria.deviceSn}
</if>
@ -42,13 +45,47 @@
<if test="criteria.createTime != null and criteria.createTime.size() != 0">
and d.create_time between #{criteria.createTime[0]} and #{criteria.createTime[1]}
</if>
<if test="criteria.tenantId != null">
AND tenant_id = #{criteria.tenantId}
</if>
and d.customer_id = #{criteria.customerId}
</where>
order by d.app_device_id desc
order by d.create_time desc
</select>
<!-- 分页查询APP/小程序设备 -->
<select id="queryAll" resultType="com.fuyuanshen.modules.system.domain.app.APPDevice">
select d.* from app_device as d
<where>
<!-- 时间范围等其他条件保持原样 -->
<if test="criteria.deviceName != null and criteria.deviceName.trim() != ''">
and d.device_name like concat('%', TRIM(#{criteria.deviceName}), '%')
</if>
<if test="criteria.deviceMac != null and criteria.deviceMac.trim() != ''">
and d.device_mac = #{criteria.deviceMac}
</if>
<if test="criteria.deviceImei != null and criteria.deviceImei.trim() != ''">
and d.device_imei = #{criteria.deviceImei}
</if>
<if test="criteria.deviceSn != null">
and d.device_sn = #{criteria.deviceSn}
</if>
<if test="criteria.deviceType != null">
and d.device_type = #{criteria.deviceType}
</if>
<if test="criteria.deviceStatus != null">
and d.device_status = #{criteria.deviceStatus}
</if>
<if test="criteria.createTime != null and criteria.createTime.size() != 0">
and d.create_time between #{criteria.createTime[0]} and #{criteria.createTime[1]}
</if>
<if test="criteria.tenantId != null">
AND tenant_id = #{criteria.tenantId}
</if>
and d.customer_id = #{criteria.customerId}
</where>
order by d.create_time desc
</select>
</mapper>

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.fuyuanshen.modules.system.mapper.APPRoleMapper">
<resultMap id="BaseResultMap" type="com.fuyuanshen.modules.system.domain.app.APPRole">
<id column="role_role_id" property="id"/>
<result column="role_name" property="name"/>
<result column="role_data_scope" property="dataScope"/>
<result column="role_level" property="level"/>
<result column="role_description" property="description"/>
<result column="role_create_by" property="createBy"/>
<result column="role_update_by" property="updateBy"/>
<result column="role_create_time" property="createTime"/>
<result column="role_update_time" property="updateTime"/>
<collection property="menus" ofType="com.fuyuanshen.modules.system.domain.app.APPMenu">
<id column="menu_id" property="id"/>
<result column="menu_title" property="title"/>
<result column="menu_permission" property="permission"/>
</collection>
</resultMap>
<sql id="Base_Column_List">
role.role_id as role_role_id, role.name as role_name, role.data_scope as role_data_scope,
role.level as role_level, role.description as role_description, role.create_by as role_create_by,
role.update_by as role_update_by, role.create_time as role_create_time, role.update_time as role_update_time
</sql>
<sql id="Menu_Column_List">
menu.menu_id as menu_id, menu.title as menu_title, menu.permission as menu_permission
</sql>
<sql id="Dept_Column_List">
dept.dept_id as dept_id, dept.name as dept_name
</sql>
<sql id="Where_sql">
<where>
<if test="criteria.blurry != null and criteria.blurry != ''">
and (
role.name like concat('%', #{criteria.blurry}, '%')
or role.description like concat('%', #{criteria.blurry}, '%')
)
</if>
<if test="criteria.createTime != null and criteria.createTime.size() != 0">
and role.create_time between #{criteria.createTime[0]} and #{criteria.createTime[1]}
</if>
</where>
</sql>
<select id="findByUserId" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>,
<include refid="Menu_Column_List"/>
from app_role role
left join app_roles_menus srm on role.role_id = srm.role_id
left join app_menu menu on menu.menu_id = srm.menu_id
left join app_users_roles ur on role.role_id = ur.role_id
WHERE role.role_id = ur.role_id AND ur.user_id = #{userId}
</select>
</mapper>

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fuyuanshen.modules.system.mapper.app.AppDeviceTypeMapper">
<resultMap id="BaseResultMap" type="com.fuyuanshen.modules.system.domain.app.APPDeviceType">
<id property="id" column="id"/>
<result property="typeName" column="type_name"/>
<result property="isSupportBle" column="is_support_ble"/>
<result property="locateMode" column="locate_mode"/>
<result property="networkWay" column="network_way"/>
<result property="createBy" column="create_by"/>
<result property="updateBy" column="update_by"/>
<result property="createTime" column="create_time"/>
<result property="updateTime" column="update_time"/>
<result property="customerId" column="customer_id"/>
<result property="communicationMode" column="communication_mode"/>
</resultMap>
<sql id="Base_Column_List">
id,type_name,is_support_ble,locate_mode,network_way,create_by,
update_by,create_time,update_time,customer_id,communication_mode
</sql>
<!-- 查询设备类型列表 -->
<select id="appTypeList" resultType="com.fuyuanshen.modules.system.domain.app.APPDeviceType">
select d.* from app_device_type as d
<where>
<!-- 时间范围等其他条件保持原样 -->
<if test="criteria.deviceName != null and criteria.deviceName.trim() != ''">
and d.device_name like concat('%', TRIM(#{criteria.deviceName}), '%')
</if>
<if test="criteria.deviceMac != null and criteria.deviceMac.trim() != ''">
and d.device_mac = #{criteria.deviceMac}
</if>
<if test="criteria.deviceImei != null and criteria.deviceImei.trim() != ''">
and d.device_imei = #{criteria.deviceImei}
</if>
<if test="criteria.deviceSn != null">
and d.device_sn = #{criteria.deviceSn}
</if>
<if test="criteria.deviceType != null">
and d.device_type = #{criteria.deviceType}
</if>
<if test="criteria.deviceStatus != null">
and d.device_status = #{criteria.deviceStatus}
</if>
<if test="criteria.createTime != null and criteria.createTime.size() != 0">
and d.create_time between #{criteria.createTime[0]} and #{criteria.createTime[1]}
</if>
<if test="criteria.tenantId != null">
AND tenant_id = #{criteria.tenantId}
</if>
and d.customer_id = #{criteria.customerId}
</where>
order by d.create_time desc
</select>
</mapper>

View File

@ -39,11 +39,14 @@
<!-- 分页查询设备 -->
<select id="findAll" resultType="com.fuyuanshen.modules.system.domain.Device">
select
<include refid="device_Column_List"/>,
t.type_name as typeName
d.id,d.device_name,
d.device_pic, d.device_mac, d.device_sn, d.update_by,d.device_imei,
d.update_time, d.device_type, d.remark, d.binding_status,t.type_name,
da.assignee_id AS customerId, da.assignee_name AS customerName, da.active AS deviceStatus,
da.assigned_at AS create_time , da.assigner_name AS create_by , da.id AS assignId
from device d
left join device_type t on d.device_type = t.id
left join device_assignments da on d.current_owner_id = da.from_customer_id
LEFT JOIN device_type t ON d.device_type = t.id
LEFT JOIN device_assignments da ON da.device_id = d.id
<where>
<!-- 时间范围等其他条件保持原样 -->
<if test="criteria.deviceName != null and criteria.deviceName.trim() != ''">
@ -52,22 +55,23 @@
<if test="criteria.deviceMac != null">
and d.device_mac = #{criteria.deviceMac}
</if>
<if test="criteria.deviceSn != null">
and d.device_sn = #{criteria.deviceSn}
<if test="criteria.deviceImei != null">
and d.device_imei = #{criteria.deviceImei}
</if>
<if test="criteria.deviceType != null">
and d.device_type = #{criteria.deviceType}
</if>
<if test="criteria.deviceStatus != null">
and d.device_status = #{criteria.deviceStatus}
and da.active = #{criteria.deviceStatus}
</if>
<if test="criteria.createTime != null and criteria.createTime.size() != 0">
and d.create_time between #{criteria.createTime[0]} and #{criteria.createTime[1]}
</if>
<if test="criteria.currentOwnerId != null">
AND (current_owner_id = #{criteria.currentOwnerId} OR da.from_customer_id = #{criteria.currentOwnerId})
<if test="criteria.tenantId != null">
AND tenant_id = #{criteria.tenantId}
</if>
AND da.assignee_id = #{criteria.currentOwnerId}
<!-- 下面这两个条件只需满足一个 -->
@ -80,15 +84,12 @@
<!-- <if test="criteria.customerId == null"> -->
<!-- AND tenant_id = #{criteria.tenantId} -->
<!-- </if> -->
</where>
order by d.id desc
ORDER BY create_time DESC
</select>
<select id="findAll1" resultType="com.fuyuanshen.modules.system.domain.Device">
select
SELECT
d.id, d.customer_id, d.customer_name, d.device_name,
d.device_pic, d.device_mac, d.device_sn, d.create_by, d.update_by,
d.create_time, d.update_time, d.device_type, d.remark, d.device_status, t.type_name

View File

@ -44,8 +44,8 @@
<!-- 查询所有设备类型 -->
<select id="findAll" resultMap="BaseResultMap">
SELECT dt.*
FROM device_type dt
SELECT DISTINCT dt.*
FROM device_type dt
JOIN device_type_grants dg ON dt.id = dg.device_type_id
<where>
<if test="criteria.typeName != null and criteria.typeName.trim() != ''">

View File

@ -223,6 +223,14 @@
<artifactId>commons-text</artifactId>
<version>1.13.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-mqtt</artifactId>
</dependency>
</dependencies>
<build>