1
0

30 Commits

Author SHA1 Message Date
a7340c744e APP客户设备绑定-绑定时间 2025-07-19 14:22:36 +08:00
3feafc2cd9 删除设备类型 2025-07-19 10:20:07 +08:00
f2921ff12f 分页查询设备-设备类型 2025-07-19 09:17:14 +08:00
ec89dd8c1e 新增设备 校验 2025-07-18 18:12:36 +08:00
57322c9c87 蓝牙名称 2025-07-18 17:57:58 +08:00
7d91426414 Merge branch 'dyf-device' 2025-07-18 15:17:18 +08:00
ad06811747 撤回设备 2025-07-18 15:06:39 +08:00
dyf
aeaa906bc9 Merge pull request 'feat(fys-demo): 添加视频上传和处理功能' (#5) from liwenlong/fys-Multi-tenant:jingquan into main
Reviewed-on: dyf/fys-Multi-tenant#5
2025-07-18 14:03:18 +08:00
36092932bf feat(fys-demo): 添加视频上传和处理功能
- 新增 VideoUploadController 控制器处理视频上传
- 添加视频帧提取和转换为 RGB565 格式功能
- 实现视频文件大小和格式校验
- 优化临时文件创建和清理逻辑
- 引入 javacv-platform依赖进行视频处理
2025-07-18 13:27:26 +08:00
34188f20dd 帐号状态(0正常 1停用) 2025-07-18 11:18:30 +08:00
0fa0e4ab1b 帐号状态(0正常 1停用) 2025-07-18 10:36:02 +08:00
e321dcd652 修改客户状态 2025-07-18 10:23:04 +08:00
78e2538f71 Merge branch 'dyf-device' 2025-07-17 16:41:08 +08:00
faef79e56d return R.ok(); 2025-07-17 16:29:25 +08:00
7b1615ce4d WEB端解绑设备 2025-07-17 16:20:22 +08:00
171eeabea1 Merge branch 'dyf-device' 2025-07-17 09:24:10 +08:00
cc776300ec 删除设备 逻辑修改 2025-07-17 09:23:11 +08:00
04df7e3ec7 可靠点阵生成工具 - 终极版 2025-07-16 08:46:21 +08:00
7be94fe1d2 产品参数bug修改 2025-07-15 15:59:32 +08:00
6f24759361 发送指令显示工具 2025-07-15 15:58:12 +08:00
530ee83488 新增绑定时间字段 2025-07-14 14:23:17 +08:00
f938716e2d 分页查询APP用户信息列表 2025-07-14 10:03:51 +08:00
bbf1c6cfd5 分页查询设备:设备类型自动更新 2025-07-14 09:13:30 +08:00
082f890009 登录查询用户新增用户类型查询条件 2025-07-14 09:02:13 +08:00
800b825892 登录查询用户新增用户类型查询条件 2025-07-14 08:53:53 +08:00
cfafbc54f7 Merge branch 'dyf-device' 2025-07-12 14:01:00 +08:00
fdb64b1dcc 小程序:相关接口 2025-07-12 13:57:31 +08:00
fbbe90207e 解绑问题修复 2025-07-12 10:41:13 +08:00
2f38f08538 Merge remote-tracking branch 'origin/main' 2025-07-12 09:56:42 +08:00
eb1552d982 绑定蓝牙问题修复 2025-07-12 09:56:26 +08:00
63 changed files with 1455 additions and 824 deletions

View File

@ -4,13 +4,14 @@ import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.exception.NotLoginException;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fuyuanshen.app.model.AppSmsLoginBody;
import com.fuyuanshen.app.service.AppLoginService;
import com.fuyuanshen.common.core.constant.SystemConstants;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.domain.model.AppLoginBody;
import com.fuyuanshen.common.core.domain.model.AppSmsRegisterBody;
import com.fuyuanshen.common.core.domain.model.PasswordLoginBody;
import com.fuyuanshen.common.core.domain.model.RegisterBody;
import com.fuyuanshen.common.core.domain.model.SmsLoginBody;
import com.fuyuanshen.common.core.utils.*;
import com.fuyuanshen.common.encrypt.annotation.ApiEncrypt;
import com.fuyuanshen.common.json.utils.JsonUtils;
import com.fuyuanshen.common.satoken.utils.LoginHelper;
import com.fuyuanshen.common.tenant.helper.TenantHelper;
@ -23,8 +24,8 @@ import com.fuyuanshen.system.service.ISysTenantService;
import com.fuyuanshen.web.domain.vo.LoginTenantVo;
import com.fuyuanshen.web.domain.vo.LoginVo;
import com.fuyuanshen.web.domain.vo.TenantListVo;
import com.fuyuanshen.web.service.AppRegisterService;
import com.fuyuanshen.web.service.IAuthStrategy;
import com.fuyuanshen.web.service.SysRegisterService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -50,7 +51,7 @@ import java.util.List;
public class AppAuthController {
private final AppLoginService loginService;
private final AppRegisterService registerService;
private final SysRegisterService registerService;
private final ISysConfigService configService;
private final ISysTenantService tenantService;
private final ISysClientService clientService;
@ -63,15 +64,15 @@ public class AppAuthController {
*/
// @ApiEncrypt
@PostMapping("/login")
public R<LoginVo> login(@RequestBody AppLoginBody appLoginBody) {
public R<LoginVo> login(@RequestBody AppSmsLoginBody appSmsLoginBody) {
// SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class);
ValidatorUtils.validate(appLoginBody);
PasswordLoginBody loginBody = new PasswordLoginBody();
loginBody.setUsername(appLoginBody.getUserName());
loginBody.setPassword(appLoginBody.getPassword());
loginBody.setTenantId(appLoginBody.getTenantId());
loginBody.setClientId("835b15335d389c9fcfdf99421fa8019b");
loginBody.setGrantType("appPassword");
ValidatorUtils.validate(appSmsLoginBody);
SmsLoginBody loginBody = new SmsLoginBody();
loginBody.setPhonenumber(appSmsLoginBody.getPhonenumber());
loginBody.setSmsCode(appSmsLoginBody.getSmsCode());
loginBody.setTenantId(appSmsLoginBody.getTenantId());
loginBody.setClientId("ca839698e245d60aa2f0e59bd52b34f8");
loginBody.setGrantType("appSms");
// 授权类型和客户端id
String clientId = loginBody.getClientId();
String grantType = loginBody.getGrantType();
@ -106,8 +107,12 @@ public class AppAuthController {
/**
* 用户注册
*/
@ApiEncrypt
@PostMapping("/register")
public R<Void> register(@Validated @RequestBody AppSmsRegisterBody user) {
public R<Void> register(@Validated @RequestBody RegisterBody user) {
if (!configService.selectRegisterEnabled(user.getTenantId())) {
return R.fail("当前系统没有开启注册功能!");
}
registerService.register(user);
return R.ok();
}

View File

@ -1,62 +0,0 @@
package com.fuyuanshen.app.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import com.fuyuanshen.app.domain.dto.APPForgotPasswordDTO;
import com.fuyuanshen.app.domain.dto.APPForgotPasswordSmsDTO;
import com.fuyuanshen.app.domain.dto.APPUpdateUserDTO;
import com.fuyuanshen.app.domain.vo.APPUserInfoVo;
import com.fuyuanshen.app.service.IAppUserService;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.web.core.BaseController;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* APP 用户管理
* @date 2025-06-27
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/app/user")
public class AppUserController extends BaseController {
private final IAppUserService appUserService;
/**
* 个人中心
*/
@GetMapping("/getUserInfo")
public R<APPUserInfoVo> getUserInfo() {
return R.ok(appUserService.getUserInfo());
}
/**
* 修改个人信息
*/
@PostMapping("/updateUser")
public R<Void> updateUser(@Validated @ModelAttribute APPUpdateUserDTO bo) {
return toAjax(appUserService.updateUser(bo));
}
/**
* 忘记密码
*/
@SaIgnore
@PostMapping("/forgotPassword")
public R<Void> forgotPassword(@RequestBody APPForgotPasswordDTO bo) {
return toAjax(appUserService.forgotPassword(bo));
}
/**
* 发送忘记密码短信
*/
@SaIgnore
@PostMapping("/sendForgotPasswordSms")
public R<Void> sendForgotPasswordSms(@Validated @RequestBody APPForgotPasswordSmsDTO dto) throws Exception {
return toAjax(appUserService.sendForgotPasswordSms(dto));
}
}

View File

@ -22,4 +22,5 @@ public class AppSmsLoginBody {
*/
@NotBlank(message = "租户ID不能为空")
private String tenantId;
}

View File

@ -2,11 +2,14 @@ package com.fuyuanshen.app.service;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fuyuanshen.app.domain.vo.AppRoleVo;
import com.fuyuanshen.app.domain.vo.AppUserVo;
import com.fuyuanshen.common.core.constant.Constants;
import com.fuyuanshen.common.core.constant.SystemConstants;
import com.fuyuanshen.common.core.constant.TenantConstants;
import com.fuyuanshen.common.core.domain.dto.RoleDTO;
import com.fuyuanshen.common.core.domain.model.AppLoginUser;
import com.fuyuanshen.common.core.enums.LoginType;
import com.fuyuanshen.common.core.exception.user.UserException;
@ -28,10 +31,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.*;
import java.util.function.Supplier;
/**

View File

@ -0,0 +1,102 @@
package com.fuyuanshen.mp.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.exception.NotLoginException;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fuyuanshen.app.model.AppSmsLoginBody;
import com.fuyuanshen.app.service.AppLoginService;
import com.fuyuanshen.common.core.constant.SystemConstants;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.domain.model.AppLoginUser;
import com.fuyuanshen.common.core.domain.model.LoginUser;
import com.fuyuanshen.common.core.domain.model.RegisterBody;
import com.fuyuanshen.common.core.domain.model.SmsLoginBody;
import com.fuyuanshen.common.core.utils.*;
import com.fuyuanshen.common.encrypt.annotation.ApiEncrypt;
import com.fuyuanshen.common.json.utils.JsonUtils;
import com.fuyuanshen.common.satoken.utils.LoginHelper;
import com.fuyuanshen.common.tenant.helper.TenantHelper;
import com.fuyuanshen.equipment.domain.UserApp;
import com.fuyuanshen.equipment.enums.AppUserTypeEnum;
import com.fuyuanshen.mp.domian.dto.AuthUserDto;
import com.fuyuanshen.mp.service.MPAuthService;
import com.fuyuanshen.mp.service.MPService;
import com.fuyuanshen.system.domain.bo.SysTenantBo;
import com.fuyuanshen.system.domain.vo.SysClientVo;
import com.fuyuanshen.system.domain.vo.SysTenantVo;
import com.fuyuanshen.system.domain.vo.SysUserVo;
import com.fuyuanshen.system.service.ISysClientService;
import com.fuyuanshen.system.service.ISysConfigService;
import com.fuyuanshen.system.service.ISysTenantService;
import com.fuyuanshen.web.domain.vo.LoginTenantVo;
import com.fuyuanshen.web.domain.vo.LoginVo;
import com.fuyuanshen.web.domain.vo.TenantListVo;
import com.fuyuanshen.web.service.IAuthStrategy;
import com.fuyuanshen.web.service.SysRegisterService;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* APP认证
*
* @author Lion Li
*/
@Slf4j
@SaIgnore
@RequiredArgsConstructor
@RestController
@RequestMapping("/mp")
public class MPAuthController {
private final AppLoginService loginService;
private final SysRegisterService registerService;
private final ISysConfigService configService;
private final ISysTenantService tenantService;
private final ISysClientService clientService;
private final MPAuthService mpAuthService;
private final MPService mpService;
@Operation(summary = "小程序登录授权")
@PostMapping(value = "/login")
public ResponseEntity<Object> login(@RequestBody AuthUserDto authUser, HttpServletRequest request) throws Exception {
Long phoneNumber = authUser.getPhoneNumber();
// 判断小程序用户是否存在,不存在创建
UserApp mpUser = mpService.getMpUser(phoneNumber);
if (mpUser == null) {
RegisterBody registerBody = new RegisterBody();
registerBody.setUsername(phoneNumber.toString());
registerBody.setUserType(AppUserTypeEnum.XCX_USER.getCode());
registerBody.setTenantId("894078");
registerBody.setPassword("123456");
mpAuthService.register(registerBody);
}
// 去登录
SysClientVo client = clientService.queryByClientId("ca839698e245d60aa2f0e59bd52b34f8");
UserApp user = mpService.loadUserByUsername(phoneNumber.toString());
LoginUser loginUser = mpAuthService.buildLoginUser(user);
LoginVo login = mpAuthService.login(loginUser, client);
// 返回登录信息
return ResponseEntity.ok(login);
}
}

View File

@ -0,0 +1,37 @@
package com.fuyuanshen.mp.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import com.fuyuanshen.common.core.domain.ResponseVO;
import com.fuyuanshen.equipment.service.DeviceService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author: 默苍璃
* @date: 2025-07-1208:36
*/
@Slf4j
@RestController
@RequestMapping("/mp")
@RequiredArgsConstructor
@Tag(name = "小程序:相关接口")
@SaIgnore
public class MPController {
private final DeviceService deviceService;
@GetMapping("/queryDevice")
@Operation(summary = "检查是否存在设备MAC号")
public ResponseVO<Boolean> queryDevice(@Parameter(name = "设备mac值") String mac) {
return ResponseVO.success(deviceService.queryDevice(mac));
}
}

View File

@ -0,0 +1,46 @@
/*
* 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.mp.domian.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
/**
* @author Zheng Jie
* @date 2018-11-30
*/
@Getter
@Setter
public class AuthUserDto {
@Schema(name = "用户名")
private String username;
@Schema(name = "手机号APP/小程序 登录)")
private Long phoneNumber;
@Schema(name = "密码")
private String password;
@Schema(name = "验证码")
private String code;
@Schema(name = "验证码的key")
private String uuid = "";
}

View File

@ -0,0 +1,8 @@
package com.fuyuanshen.mp.mapper;
/**
* @author: 默苍璃
* @date: 2025-07-1210:16
*/
public interface MPMapper {
}

View File

@ -0,0 +1,164 @@
package com.fuyuanshen.mp.service;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.fuyuanshen.app.domain.vo.AppUserVo;
import com.fuyuanshen.app.service.IAppRoleService;
import com.fuyuanshen.common.core.constant.Constants;
import com.fuyuanshen.common.core.constant.SystemConstants;
import com.fuyuanshen.common.core.constant.TenantConstants;
import com.fuyuanshen.common.core.domain.dto.PostDTO;
import com.fuyuanshen.common.core.domain.dto.RoleDTO;
import com.fuyuanshen.common.core.domain.model.AppLoginUser;
import com.fuyuanshen.common.core.domain.model.LoginUser;
import com.fuyuanshen.common.core.domain.model.PasswordLoginBody;
import com.fuyuanshen.common.core.domain.model.RegisterBody;
import com.fuyuanshen.common.core.enums.LoginType;
import com.fuyuanshen.common.core.enums.UserType;
import com.fuyuanshen.common.core.exception.user.UserException;
import com.fuyuanshen.common.core.utils.*;
import com.fuyuanshen.common.json.utils.JsonUtils;
import com.fuyuanshen.common.log.event.LogininforEvent;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
import com.fuyuanshen.common.satoken.utils.LoginHelper;
import com.fuyuanshen.common.tenant.exception.TenantException;
import com.fuyuanshen.common.tenant.helper.TenantHelper;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.UserApp;
import com.fuyuanshen.equipment.service.AppUserService;
import com.fuyuanshen.system.domain.SysUser;
import com.fuyuanshen.system.domain.bo.SysUserBo;
import com.fuyuanshen.system.domain.vo.*;
import com.fuyuanshen.system.service.ISysTenantService;
import com.fuyuanshen.system.service.ISysUserService;
import com.fuyuanshen.web.domain.vo.LoginVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.util.*;
import java.util.function.Supplier;
/**
* 登录校验方法
*
* @author Lion Li
*/
@RequiredArgsConstructor
@Slf4j
@Service
public class MPAuthService {
private final ISysUserService userService;
private final AppUserService appUserService;
/**
* 小程序注册
*/
public void register(RegisterBody registerBody) {
String tenantId = registerBody.getTenantId();
String username = registerBody.getUsername();
String password = registerBody.getPassword();
// 校验用户类型是否存在
String userType = UserType.getUserType(registerBody.getUserType()).getUserType();
UserApp sysUser = new UserApp();
sysUser.setUserName(username);
sysUser.setNickName(username);
sysUser.setPhonenumber(username);
sysUser.setTenantId(tenantId);
sysUser.setUserType(registerBody.getUserType());
sysUser.setPassword(BCrypt.hashpw(password));
sysUser.setUserType(userType);
appUserService.saveMpUser(sysUser);
}
/**
* 记录登录信息
*
* @param tenantId 租户ID
* @param username 用户名
* @param status 状态
* @param message 消息内容
* @return
*/
private void recordLogininfor(String tenantId, String username, String status, String message) {
LogininforEvent logininforEvent = new LogininforEvent();
logininforEvent.setTenantId(tenantId);
logininforEvent.setUsername(username);
logininforEvent.setStatus(status);
logininforEvent.setMessage(message);
logininforEvent.setRequest(ServletUtils.getRequest());
// com.fuyuanshen.web.listener.UserActionListener
SpringUtils.context().publishEvent(logininforEvent);
}
public LoginVo login(LoginUser loginUser, SysClientVo client) {
loginUser.setClientKey(client.getClientKey());
loginUser.setDeviceType(client.getDeviceType());
SaLoginParameter model = new SaLoginParameter();
model.setDeviceType(client.getDeviceType());
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
// 例如: 后台用户30分钟过期 app用户1天过期
model.setTimeout(client.getTimeout());
model.setActiveTimeout(client.getActiveTimeout());
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
// 生成token
LoginHelper.login(loginUser, model);
LoginVo loginVo = new LoginVo();
loginVo.setAccessToken(StpUtil.getTokenValue());
loginVo.setExpireIn(StpUtil.getTokenTimeout());
loginVo.setClientId(client.getClientId());
return loginVo;
}
/**
* 构建登录用户
*/
public LoginUser buildLoginUser(UserApp user) {
LoginUser loginUser = new LoginUser();
Long userId = user.getUserId();
loginUser.setTenantId(user.getTenantId());
loginUser.setUserId(userId);
loginUser.setDeptId(user.getDeptId());
loginUser.setUsername(user.getUserName());
loginUser.setNickname(user.getNickName());
loginUser.setUserType(user.getUserType());
// 用户级别
// loginUser.setUserLevel(user.getUserLevel());
// pid
// loginUser.setPid(user.getPid());
// loginUser.setMenuPermission(permissionService.getMenuPermission(userId));
// loginUser.setRolePermission(permissionService.getRolePermission(userId));
// if (ObjectUtil.isNotNull(user.getDeptId())) {
// Opt<SysDeptVo> deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById);
// loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY));
// loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY));
// }
// List<SysRoleVo> roles = roleService.selectRolesByUserId(userId);
// // List<SysPostVo> posts = postService.selectPostsByUserId(userId);
// loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class));
// loginUser.setPosts(BeanUtil.copyToList(posts, PostDTO.class));
return loginUser;
}
}

View File

@ -0,0 +1,26 @@
package com.fuyuanshen.mp.service;
import com.fuyuanshen.equipment.domain.UserApp;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 登录校验方法
*
* @author Lion Li
*/
public interface MPService {
/**
* 获取小程序用户信息
*
* @param phoneNumber 手机号
*/
UserApp getMpUser(Long phoneNumber);
UserApp loadUserByUsername(String username);
}

View File

@ -0,0 +1,12 @@
package com.fuyuanshen.mp.service.impl;
/**
* 登录校验方法
*
* @author Lion Li
*/
public class MPAuthServiceImpl {
}

View File

@ -0,0 +1,43 @@
package com.fuyuanshen.mp.service.impl;
import com.fuyuanshen.equipment.domain.UserApp;
import com.fuyuanshen.equipment.mapper.UserAppMapper;
import com.fuyuanshen.equipment.service.AppUserService;
import com.fuyuanshen.mp.service.MPService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 登录校验方法
*
* @author Lion Li
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class MPServiceImpl implements MPService {
private final AppUserService appUserService;
/**
* 获取小程序用户信息
*
* @param phoneNumber 手机号
*/
@Override
public UserApp getMpUser(Long phoneNumber) {
return appUserService.getMpUser(phoneNumber);
}
@Override
public UserApp loadUserByUsername(String username) {
return appUserService.loadUserByUsername(username);
}
}

View File

@ -111,6 +111,7 @@ public class AuthController {
return R.ok(loginVo);
}
/**
* 获取跳转URL
*
@ -133,6 +134,7 @@ public class AuthController {
return R.ok("操作成功", authorizeUrl);
}
/**
* 前端回调绑定授权(需要token)
*

View File

@ -1,129 +0,0 @@
package com.fuyuanshen.web.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fuyuanshen.app.domain.AppUser;
import com.fuyuanshen.app.domain.vo.AppUserVo;
import com.fuyuanshen.app.mapper.AppUserMapper;
import com.fuyuanshen.common.core.constant.Constants;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.domain.model.AppSmsRegisterBody;
import com.fuyuanshen.common.core.exception.BadRequestException;
import com.fuyuanshen.common.core.exception.user.CaptchaException;
import com.fuyuanshen.common.core.exception.user.CaptchaExpireException;
import com.fuyuanshen.common.core.exception.user.UserException;
import com.fuyuanshen.common.core.utils.MessageUtils;
import com.fuyuanshen.common.core.utils.ServletUtils;
import com.fuyuanshen.common.core.utils.SpringUtils;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.log.event.LogininforEvent;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.common.tenant.helper.TenantHelper;
import com.fuyuanshen.common.web.config.properties.CaptchaProperties;
import com.fuyuanshen.system.mapper.SysUserMapper;
import com.fuyuanshen.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Date;
/**
* 注册校验方法
*
* @author Lion Li
*/
@RequiredArgsConstructor
@Service
public class AppRegisterService {
private final ISysUserService userService;
private final SysUserMapper userMapper;
private final CaptchaProperties captchaProperties;
private final AppUserMapper appUserMapper;
/**
* 注册
*/
public void register(AppSmsRegisterBody registerBody) {
String phoneNumber = registerBody.getPhoneNumber();
LambdaQueryWrapper<AppUser> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(AppUser::getPhonenumber, phoneNumber);
AppUserVo appUserVo = appUserMapper.selectVoOne(wrapper);
if (appUserVo != null) {
throw new BadRequestException("该手机号已被注册");
}
String verificationCode = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phoneNumber);
if (verificationCode == null) {
throw new BadRequestException("验证码已过期");
}
if(!registerBody.getVerificationCode().equals(verificationCode)){
throw new BadRequestException("验证码错误");
}
String tenantId = registerBody.getTenantId();
String username = registerBody.getPhoneNumber();
String password = registerBody.getPassword();
AppUser appUser = new AppUser();
appUser.setUserName(username);
appUser.setNickName(username);
appUser.setPhonenumber(phoneNumber);
appUser.setPassword(password);
appUser.setUserType("app_user");
appUser.setTenantId(tenantId);
appUser.setLoginIp(ServletUtils.getClientIP());
appUser.setStatus("0");
appUser.setDelFlag("0");
appUser.setCreateTime(new Date());
boolean exist = TenantHelper.dynamic(tenantId, () -> {
return appUserMapper.exists(new LambdaQueryWrapper<AppUser>()
.eq(AppUser::getUserName, appUser.getUserName()));
});
if (exist) {
throw new UserException("user.register.save.error", username);
}
appUserMapper.insert(appUser);
recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success"));
}
/**
* 校验验证码
*
* @param username 用户名
* @param code 验证码
* @param uuid 唯一标识
*/
public void validateCaptcha(String tenantId, String username, String code, String uuid) {
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
String captcha = RedisUtils.getCacheObject(verifyKey);
RedisUtils.deleteObject(verifyKey);
if (captcha == null) {
recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
throw new CaptchaExpireException();
}
if (!code.equalsIgnoreCase(captcha)) {
recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
throw new CaptchaException();
}
}
/**
* 记录登录信息
*
* @param tenantId 租户ID
* @param username 用户名
* @param status 状态
* @param message 消息内容
* @return
*/
private void recordLogininfor(String tenantId, String username, String status, String message) {
LogininforEvent logininforEvent = new LogininforEvent();
logininforEvent.setTenantId(tenantId);
logininforEvent.setUsername(username);
logininforEvent.setStatus(status);
logininforEvent.setMessage(message);
logininforEvent.setRequest(ServletUtils.getRequest());
SpringUtils.context().publishEvent(logininforEvent);
}
}

View File

@ -1,122 +0,0 @@
package com.fuyuanshen.web.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fuyuanshen.app.domain.AppUser;
import com.fuyuanshen.app.domain.vo.AppUserVo;
import com.fuyuanshen.app.mapper.AppUserMapper;
import com.fuyuanshen.app.service.AppLoginService;
import com.fuyuanshen.common.core.constant.Constants;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.constant.SystemConstants;
import com.fuyuanshen.common.core.domain.model.AppLoginUser;
import com.fuyuanshen.common.core.domain.model.PasswordLoginBody;
import com.fuyuanshen.common.core.enums.LoginType;
import com.fuyuanshen.common.core.exception.user.CaptchaException;
import com.fuyuanshen.common.core.exception.user.CaptchaExpireException;
import com.fuyuanshen.common.core.exception.user.UserException;
import com.fuyuanshen.common.core.utils.MessageUtils;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.core.utils.ValidatorUtils;
import com.fuyuanshen.common.json.utils.JsonUtils;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
import com.fuyuanshen.common.satoken.utils.LoginHelper;
import com.fuyuanshen.common.tenant.helper.TenantHelper;
import com.fuyuanshen.common.web.config.properties.CaptchaProperties;
import com.fuyuanshen.system.domain.vo.SysClientVo;
import com.fuyuanshen.web.domain.vo.LoginVo;
import com.fuyuanshen.web.service.IAuthStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 密码认证策略
*
* @author Michelle.Chung
*/
@Slf4j
@Service("appPassword" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor
public class AppPasswordAuthStrategy implements IAuthStrategy {
private final CaptchaProperties captchaProperties;
private final AppLoginService loginService;
private final AppUserMapper appUserMapper;
@Override
public LoginVo login(String body, SysClientVo client) {
PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class);
ValidatorUtils.validate(loginBody);
String tenantId = loginBody.getTenantId();
String username = loginBody.getUsername();
String password = loginBody.getPassword();
String code = loginBody.getCode();
String uuid = loginBody.getUuid();
// boolean captchaEnabled = captchaProperties.getEnable();
// // 验证码开关
// if (captchaEnabled) {
// validateCaptcha(tenantId, username, code, uuid);
// }
AppLoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
AppUserVo user = loadUserByUsername(username);
loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !password.equals(user.getPassword()));
// 此处可根据登录用户的数据不同 自行创建 loginUser
return loginService.buildLoginUser(user);
});
loginUser.setClientKey(client.getClientKey());
loginUser.setDeviceType(client.getDeviceType());
SaLoginParameter model = new SaLoginParameter();
model.setDeviceType(client.getDeviceType());
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
// 例如: 后台用户30分钟过期 app用户1天过期
model.setTimeout(client.getTimeout());
model.setActiveTimeout(client.getActiveTimeout());
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
// 生成token
AppLoginHelper.login(loginUser, model);
LoginVo loginVo = new LoginVo();
loginVo.setAccessToken(StpUtil.getTokenValue());
loginVo.setExpireIn(StpUtil.getTokenTimeout());
loginVo.setClientId(client.getClientId());
return loginVo;
}
/**
* 校验验证码
*
* @param username 用户名
* @param code 验证码
* @param uuid 唯一标识
*/
private void validateCaptcha(String tenantId, String username, String code, String uuid) {
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
String captcha = RedisUtils.getCacheObject(verifyKey);
RedisUtils.deleteObject(verifyKey);
if (captcha == null) {
loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
throw new CaptchaExpireException();
}
if (!code.equalsIgnoreCase(captcha)) {
loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
throw new CaptchaException();
}
}
private AppUserVo loadUserByUsername(String username) {
AppUserVo user = appUserMapper.selectVoOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getUserName, username));
if (ObjectUtil.isNull(user)) {
log.info("登录用户:{} 不存在.", username);
throw new UserException("user.not.exists", username);
} else if (SystemConstants.DISABLE.equals(user.getStatus())) {
log.info("登录用户:{} 已被停用.", username);
throw new UserException("user.blocked", username);
}
return user;
}
}

View File

@ -14,12 +14,10 @@ import com.fuyuanshen.common.core.constant.SystemConstants;
import com.fuyuanshen.common.core.domain.model.AppLoginUser;
import com.fuyuanshen.common.core.domain.model.SmsLoginBody;
import com.fuyuanshen.common.core.enums.LoginType;
import com.fuyuanshen.common.core.enums.UserType;
import com.fuyuanshen.common.core.exception.user.CaptchaExpireException;
import com.fuyuanshen.common.core.exception.user.UserException;
import com.fuyuanshen.common.core.utils.MessageUtils;
import com.fuyuanshen.common.core.utils.ServletUtils;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.core.utils.ValidatorUtils;
import com.fuyuanshen.common.core.utils.*;
import com.fuyuanshen.common.json.utils.JsonUtils;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
@ -60,9 +58,9 @@ public class AppSmsAuthStrategy implements IAuthStrategy {
AppUserVo user = loadUserByPhonenumber(phonenumber);
if (ObjectUtil.isNull(user)) {
//新增Appuser
addAppUser(tenantId, phonenumber);
user = appUserMapper.selectVoOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getPhonenumber, phonenumber));
AppUser appUser = addAppUser(tenantId, phonenumber);
MapstructUtils.convert(appUser, user);
// user = appUserMapper.selectVoOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getPhonenumber, phonenumber));
// loginService.recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.not.exists", phonenumber));
// throw new UserException("user.not.exists", phonenumber);
}
@ -89,7 +87,7 @@ public class AppSmsAuthStrategy implements IAuthStrategy {
return loginVo;
}
private void addAppUser(String tenantId, String phonenumber) {
private AppUser addAppUser(String tenantId, String phonenumber) {
AppUser appUser = new AppUser();
appUser.setPhonenumber(phonenumber);
appUser.setUserName(phonenumber);
@ -98,6 +96,7 @@ public class AppSmsAuthStrategy implements IAuthStrategy {
appUser.setLoginIp(ServletUtils.getClientIP());
appUser.setTenantId(tenantId);
appUserMapper.insert(appUser);
return appUser;
}
@ -114,7 +113,9 @@ public class AppSmsAuthStrategy implements IAuthStrategy {
}
private AppUserVo loadUserByPhonenumber(String phonenumber) {
AppUserVo user = appUserMapper.selectVoOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getPhonenumber, phonenumber));
AppUserVo user = appUserMapper.selectVoOne(new LambdaQueryWrapper<AppUser>()
.eq(AppUser::getPhonenumber, phonenumber)
.eq(AppUser::getUserType, UserType.APP_USER.getUserType()));
if (ObjectUtil.isNull(user)) {
log.info("登录用户:{} 不存在.", phonenumber);
// throw new UserException("user.not.exists", phonenumber);
@ -126,5 +127,4 @@ public class AppSmsAuthStrategy implements IAuthStrategy {
}
return user;
}
}

View File

@ -49,9 +49,9 @@ spring:
driverClassName: com.mysql.cj.jdbc.Driver
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
url: jdbc:mysql://120.79.224.186:3366/fys-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
url: jdbc:mysql://47.120.79.150:3306/fys-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: root
password: 1fys@QWER..
password: Jq_123456#
# # 从库数据源
# slave:
# lazy: true
@ -98,13 +98,13 @@ spring:
spring.data:
redis:
# 地址
host: 120.79.224.186
host: 47.120.79.150
# 端口默认为6379
port: 26379
port: 6379
# 数据库索引
database: 2
# redis 密码必须配置
password: 1fys@QWER..
password: xhYc_djkl382^#780!
# 连接超时时间
timeout: 10s
# 是否开启ssl
@ -300,8 +300,8 @@ file:
# MQTT配置
mqtt:
username: admin
password: fys123456
url: tcp://47.107.152.87:1883
password: #YtvpSfCNG
url: tcp://47.120.79.150:2883
subClientId: fys_subClient
subTopic: worker/alert/#,worker/location/#
pubTopic: worker/location

View File

@ -1,7 +1,7 @@
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
port: 8001
port: 8000
servlet:
# 应用的访问路径
context-path: /

View File

@ -17,11 +17,6 @@ public interface GlobalConstants {
*/
String CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "captcha_codes:";
/**
* 验证码 redis key
*/
String APP_FORGOT_PASSWORD_SMS_KEY = GLOBAL_REDIS_KEY + "app_sms_forgotPassword:";
/**
* 防重提交 redis key
*/

View File

@ -1,22 +0,0 @@
package com.fuyuanshen.common.core.domain.model;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class AppLoginBody {
/**
* 手机号不能为空
*/
@NotBlank(message = "手机号不能为空")
private String userName;
/**
* 密码不能为空
*/
@NotBlank(message = "密码不能为空")
private String password;
private String tenantId;
}

View File

@ -1,22 +0,0 @@
package com.fuyuanshen.common.core.domain.model;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class AppSmsRegisterBody {
@NotBlank(message = "手机号不能为空")
private String phoneNumber;
@NotBlank(message = "密码不能为空")
private String password;
@NotBlank(message = "验证码不能为空")
private String verificationCode;
/**
* 租户ID
*/
private String tenantId;
}

View File

@ -18,14 +18,14 @@ public class PasswordLoginBody extends LoginBody {
* 用户名
*/
@NotBlank(message = "{user.username.not.blank}")
// @Length(min = 2, max = 30, message = "{user.username.length.valid}")
@Length(min = 2, max = 30, message = "{user.username.length.valid}")
private String username;
/**
* 用户密码
*/
@NotBlank(message = "{user.password.not.blank}")
// @Length(min = 5, max = 30, message = "{user.password.length.valid}")
@Length(min = 5, max = 30, message = "{user.password.length.valid}")
private String password;
}

View File

@ -21,7 +21,13 @@ public enum UserType {
/**
* 移动客户端用户
*/
APP_USER("app_user");
APP_USER("app_user"),
/**
* 小程序 用户
*/
XCX_USER("xcx_user");
/**
* 用户类型标识(用于 token、权限识别等

View File

@ -37,7 +37,7 @@ public enum BusinessType {
EXPORT,
/**
* 导入
*
*/
IMPORT,

View File

@ -17,8 +17,8 @@ public class TestSMSController {
public void testSend() {
// 在创建完SmsBlend实例后再未手动调用注销的情况下框架会持有该实例可以直接通过指定configId来获取想要的配置如果你想使用
// 负载均衡形式获取实例只要使用getSmsBlend的无参重载方法即可如果你仅有一个配置也可以使用该方法
SmsBlend smsBlend = SmsFactory.getSmsBlend("alibaba");
SmsResponse smsResponse = smsBlend.sendMessage("18656573389", "123");
SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
SmsResponse smsResponse = smsBlend.sendMessage("18656573389", "1234");
}
}

View File

@ -2,6 +2,7 @@ package com.fuyuanshen.app.controller;
import java.util.List;
import com.fuyuanshen.common.core.enums.UserType;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
@ -36,12 +37,14 @@ public class AppUserController extends BaseController {
private final IAppUserService appUserService;
/**
* 查询APP用户信息列表
*/
// @SaCheckPermission("app:user:list")
@GetMapping("/list")
public TableDataInfo<AppUserVo> list(AppUserBo bo, PageQuery pageQuery) {
bo.setUserType(UserType.APP_USER.getUserType());
return appUserService.queryPageList(bo, pageQuery);
}

View File

@ -0,0 +1,75 @@
// package com.fuyuanshen.app.controller.mp;
//
// import io.swagger.v3.oas.annotations.tags.Tag;
// import lombok.RequiredArgsConstructor;
// import lombok.extern.slf4j.Slf4j;
// import org.springframework.http.ResponseEntity;
// 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 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
// @Tag(name = "小程序:相关接口")
// 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

@ -1,14 +1,13 @@
package com.fuyuanshen.app.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fuyuanshen.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
import java.util.Date;
/**
* APP用户信息对象 app_user
@ -96,9 +95,5 @@ public class AppUser extends TenantEntity {
*/
private String remark;
/**
* 地区
*/
private String region;
}

View File

@ -1,19 +0,0 @@
package com.fuyuanshen.app.domain.dto;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class APPForgotPasswordDTO {
@NotBlank(message = "手机号不能为空")
private String phoneNumber;
@NotBlank(message = "密码不能为空")
private String password;
@NotBlank(message = "验证码不能为空")
private String verificationCode;
}

View File

@ -1,12 +0,0 @@
package com.fuyuanshen.app.domain.dto;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class APPForgotPasswordSmsDTO {
@NotBlank(message = "手机号不能为空")
private String phoneNumber;
}

View File

@ -1,6 +1,8 @@
package com.fuyuanshen.app.domain.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;

View File

@ -1,33 +0,0 @@
package com.fuyuanshen.app.domain.dto;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
/**
* @author: 默苍璃
* @date: 2025-06-1818:36
*/
@Data
public class APPUpdateUserDTO {
/**
* 用户昵称
*/
private String nickName;
/**
* 用户地区
*/
private String region;
/**
* 用户性别
*/
private String gender;
/**
* 用户头像
*/
private MultipartFile file;
}

View File

@ -1,39 +0,0 @@
package com.fuyuanshen.app.domain.vo;
import lombok.Data;
@Data
public class APPUserInfoVo {
/**
* 用户ID
*/
private Long id;
/**
* 用户昵称
*/
private String nickName;
/**
* 性别
*/
private String gender;
/**
* 手机号码
*/
private String phone;
/**
* 头像地址
*/
private String avatarPath;
/**
* 地区
*/
private String region;
}

View File

@ -1,6 +1,9 @@
package com.fuyuanshen.app.domain.vo;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fuyuanshen.app.domain.AppUser;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
@ -120,4 +123,10 @@ public class AppUserVo implements Serializable {
* 部门ID
*/
private Long deptId;
/**
* 创建时间
*/
private Date createTime;
}

View File

@ -14,5 +14,4 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface AppUserMapper extends BaseMapperPlus<AppUser, AppUserVo> {
AppUser appFindByUsername(String phoneNumber);
}

View File

@ -1,13 +1,9 @@
package com.fuyuanshen.app.service;
import com.fuyuanshen.app.domain.bo.AppUserBo;
import com.fuyuanshen.app.domain.dto.APPForgotPasswordDTO;
import com.fuyuanshen.app.domain.dto.APPForgotPasswordSmsDTO;
import com.fuyuanshen.app.domain.dto.APPUpdateUserDTO;
import com.fuyuanshen.app.domain.vo.APPUserInfoVo;
import com.fuyuanshen.app.domain.vo.AppUserVo;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.fuyuanshen.app.domain.bo.AppUserBo;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
@ -69,12 +65,4 @@ public interface IAppUserService {
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
APPUserInfoVo getUserInfo();
int updateUser(APPUpdateUserDTO bo);
int forgotPassword(APPForgotPasswordDTO bo);
int sendForgotPasswordSms(APPForgotPasswordSmsDTO dto);
}

View File

@ -1,37 +1,24 @@
package com.fuyuanshen.app.service.impl;
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.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fuyuanshen.app.domain.AppUser;
import com.fuyuanshen.app.domain.bo.AppUserBo;
import com.fuyuanshen.app.domain.dto.APPForgotPasswordDTO;
import com.fuyuanshen.app.domain.dto.APPForgotPasswordSmsDTO;
import com.fuyuanshen.app.domain.dto.APPUpdateUserDTO;
import com.fuyuanshen.app.domain.vo.APPUserInfoVo;
import com.fuyuanshen.app.domain.vo.AppUserVo;
import com.fuyuanshen.app.mapper.AppUserMapper;
import com.fuyuanshen.app.service.IAppUserService;
import com.fuyuanshen.common.core.constant.GlobalConstants;
import com.fuyuanshen.common.core.domain.model.AppLoginUser;
import com.fuyuanshen.common.core.exception.BadRequestException;
import com.fuyuanshen.common.core.utils.MapstructUtils;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.redis.utils.RedisUtils;
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
import com.fuyuanshen.system.domain.vo.SysOssVo;
import com.fuyuanshen.system.service.ISysOssService;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import com.fuyuanshen.app.domain.bo.AppUserBo;
import com.fuyuanshen.app.domain.vo.AppUserVo;
import com.fuyuanshen.app.domain.AppUser;
import com.fuyuanshen.app.mapper.AppUserMapper;
import com.fuyuanshen.app.service.IAppUserService;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
* Service业务层处理
@ -46,8 +33,6 @@ public class AppUserServiceImpl implements IAppUserService {
private final AppUserMapper baseMapper;
private final ISysOssService sysOssService;
/**
* 查询APP用户信息
*
@ -154,71 +139,4 @@ public class AppUserServiceImpl implements IAppUserService {
}
return baseMapper.deleteByIds(ids) > 0;
}
@Override
public APPUserInfoVo getUserInfo() {
String username = AppLoginHelper.getUsername();
QueryWrapper<AppUser> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_name", username);
List<AppUser> appUsers = baseMapper.selectList(queryWrapper);
if(appUsers.isEmpty()){
throw new BadRequestException("用户不存在");
}
AppUser user = appUsers.get(0);
// AppUserVo user = baseMapper.selectVoById(userId);
APPUserInfoVo appUserVo = new APPUserInfoVo();
appUserVo.setId(user.getUserId());
appUserVo.setNickName(user.getNickName());
appUserVo.setGender(user.getSex());
appUserVo.setPhone(user.getPhonenumber());
if(user.getAvatar() != null){
SysOssVo oss = sysOssService.getById(user.getAvatar());
if(oss != null){
appUserVo.setAvatarPath(oss.getUrl());
}
}
return appUserVo;
}
@Override
public int updateUser(APPUpdateUserDTO bo) {
AppLoginUser appUser = AppLoginHelper.getLoginUser();
AppUserVo appUserVo = baseMapper.selectVoById(appUser.getUserId());
if(appUserVo == null){
throw new BadRequestException("用户不存在");
}
AppUser updUser= new AppUser();
updUser.setUserId(appUser.getUserId());
updUser.setNickName(bo.getNickName());
SysOssVo oss = sysOssService.upload(bo.getFile());
updUser.setAvatar(oss.getOssId());
updUser.setRegion(bo.getRegion());
updUser.setSex(bo.getGender());
return baseMapper.update(updUser, new LambdaQueryWrapper<AppUser>().eq(AppUser::getUserId, appUser.getUserId()));
}
@Override
public int forgotPassword(APPForgotPasswordDTO bo) {
AppUser appUser = baseMapper.appFindByUsername(bo.getPhoneNumber());
if (appUser == null) {
throw new BadRequestException("手机号不存在");
}
String verificationCode = RedisUtils.getCacheObject(GlobalConstants.APP_FORGOT_PASSWORD_SMS_KEY + bo.getPhoneNumber());
if (verificationCode == null) {
throw new BadRequestException("验证码已过期");
}
if(!bo.getVerificationCode().equals(verificationCode)){
throw new BadRequestException("验证码错误");
}
appUser.setPassword(bo.getPassword());
baseMapper.updateById(appUser);
return 1;
}
@Override
public int sendForgotPasswordSms(APPForgotPasswordSmsDTO dto) {
return 1;
}
}

View File

@ -6,15 +6,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="queryAppFileList" resultType="com.fuyuanshen.app.domain.vo.AppFileVo">
select a.id,a.business_id,a.file_id,b.file_name,b.url fileUrl from app_business_file a left join sys_oss b on a.file_id = b.oss_id
where
where 1=1
<if test="businessId != null">
and a.business_id = #{businessId}
</if>
<if test="fileId != null">
and a.file_id = #{fileId}
</if>
<if test="fileType != null">
and a.file_type = #{fileType}
</if>
<if test="createBy != null">
a.create_by = #{createBy}
and a.create_by = #{createBy}
</if>
order by a.create_time desc
</select>

View File

@ -4,7 +4,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fuyuanshen.app.mapper.AppUserMapper">
<select id="appFindByUsername" resultType="com.fuyuanshen.app.domain.AppUser">
select * from app_user where user_name = #{phoneNumber}
</select>
</mapper>

View File

@ -3,6 +3,7 @@ package com.fuyuanshen.customer.controller;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.domain.ResponseVO;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
@ -51,43 +52,43 @@ public class CustomerController {
@GetMapping(value = "/allCustomer")
@Operation(summary = "查询所有客户")
public ResponseVO<List<Customer>> queryAllCustomer(UserQueryCriteria criteria) {
public R<List<Customer>> queryAllCustomer(UserQueryCriteria criteria) {
List<Customer> customers = customerService.queryAllCustomers(criteria);
return ResponseVO.success(customers);
return R.ok(customers);
}
// @Log("新增客户")
@Operation(summary = "新增客户")
@PostMapping(value = "/addCustomer")
public ResponseVO<Object> addCustomer(@Validated @RequestBody Customer customer) throws BadRequestException {
public R<Void> addCustomer(@Validated @RequestBody Customer customer) throws BadRequestException {
if (StringUtils.isBlank(customer.getPassword())) {
throw new BadRequestException("账号密码不能为空");
}
customer.setPassword(BCrypt.hashpw(customer.getPassword()));
customerService.addCustomer(customer);
return ResponseVO.success("新增客户成功!!!");
return R.ok();
}
// @Log("修改客户")
@Operation(summary = "修改客户")
@PutMapping(value = "updateCustomer")
public ResponseVO<Object> updateCustomer(@RequestBody Customer resources) throws Exception {
public R<Void> updateCustomer(@RequestBody Customer resources) throws Exception {
customerService.updateCustomer(resources);
return ResponseVO.success(null);
return R.ok();
}
// @Log("删除客户")
@Operation(summary = "删除客户")
@DeleteMapping(value = "/deleteCustomer")
public ResponseVO<Object> deleteCustomer(@RequestBody Set<Long> ids) throws BadRequestException {
public R<Void> deleteCustomer(@RequestBody Set<Long> ids) throws BadRequestException {
if (CollectionUtil.isEmpty(ids)) {
throw new BadRequestException("请选择要删除的客户");
}
customerService.delete(ids);
return ResponseVO.success(null);
return R.ok();
}

View File

@ -50,6 +50,12 @@ public class UserQueryCriteria extends BaseEntity {
@Schema(name = "是否启用")
private Boolean enabled;
/**
* 帐号状态0正常 1停用
*/
@Schema(name = "帐号状态0正常 1停用")
private String status;
@Schema(name = "部门ID")
private Long deptId;

View File

@ -58,6 +58,12 @@ public class ConsumerVo extends TenantEntity {
@Schema(name = "是否启用")
private Boolean enabled;
/**
* 帐号状态0正常 1停用
*/
@Schema(name = "帐号状态0正常 1停用")
private String status;
@Schema(name = "是否为admin账号", hidden = true)
private Boolean isAdmin = false;

View File

@ -112,6 +112,11 @@ public class CustomerServiceImpl extends ServiceImpl<CustomerMapper, Customer> i
@Override
@Transactional(rollbackFor = Exception.class)
public void updateCustomer(Customer resources) throws Exception {
if (resources.getEnabled()) {
resources.setStatus("0");
} else {
resources.setStatus("1");
}
saveOrUpdate(resources);
}

View File

@ -68,8 +68,8 @@
<if test="criteria.id != null">
and u1.user_id = #{criteria.id}
</if>
<if test="criteria.enabled != null">
and u1.enabled = #{criteria.enabled}
<if test="criteria.status != null">
and u.status = #{criteria.status}
</if>
<if test="criteria.deptIds != null and criteria.deptIds.size() != 0">
and u1.dept_id in
@ -94,7 +94,7 @@
<!-- 分页查询客户 -->
<select id="findCustomers" resultType="com.fuyuanshen.customer.domain.Customer">
select
u.user_id as customerId, u.nick_name , u.user_name, u.enabled, u.create_time
u.user_id as customerId, u.nick_name , u.user_name, u.enabled, u.create_time,u.status
from sys_user u
<where>
<if test="criteria.ids != null and !criteria.ids.isEmpty()">
@ -109,8 +109,8 @@
<if test="criteria.blurry != null and criteria.blurry.trim() != ''">
and u.nick_name like concat('%', TRIM(#{criteria.blurry}), '%')
</if>
<if test="criteria.enabled != null">
and u.enabled = #{criteria.enabled}
<if test="criteria.status != null">
and u.status = #{criteria.status}
</if>
<if test="criteria.params.beginTime != null and criteria.params.endTime != null">
and u.create_time between #{criteria.params.beginTime} and #{criteria.params.endTime}
@ -139,8 +139,8 @@
<if test="criteria.blurry != null and criteria.blurry.trim() != ''">
and u.nick_name like concat('%', TRIM(#{criteria.blurry}), '%')
</if>
<if test="criteria.enabled != null">
and u.enabled = #{criteria.enabled}
<if test="criteria.status != null">
and u.status = #{criteria.status}
</if>
<if test="criteria.params.beginTime != null and criteria.params.endTime != null">
and u.create_time between #{criteria.params.beginTime} and #{criteria.params.endTime}

View File

@ -103,6 +103,11 @@
<artifactId>fys-common-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,244 @@
package com.fuyuanshen.demo.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import com.fuyuanshen.common.core.domain.R;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.file.Files;
import java.util.Base64;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameUtils;
import org.springframework.http.ResponseEntity;
@RestController
@RequestMapping("/video")
public class VideoUploadController {
// 可配置项:建议从 application.yml 中读取
private static final int MAX_VIDEO_SIZE = 10 * 1024 * 1024; // 10 MB
private static final int FRAME_RATE = 15; // 每秒抽15帧
private static final int DURATION = 2; // 抽2秒
private static final int TOTAL_FRAMES = FRAME_RATE * DURATION;
private static final int WIDTH = 160;
private static final int HEIGHT = 80;
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@SaIgnore
public R<List<String>> upload(@RequestParam("file") MultipartFile file) {
if (file == null || file.isEmpty()) {
return R.fail("上传文件不能为空");
}
if (!isVideo(file.getOriginalFilename())) {
return R.fail("只允许上传视频文件");
}
if (file.getSize() > MAX_VIDEO_SIZE) {
return R.fail("视频大小不能超过10MB");
}
File tempFile = null;
try {
// 创建临时文件保存上传的视频
tempFile = createTempVideoFile(file);
List<BufferedImage> frames = extractFramesFromVideo(tempFile);
if (frames.isEmpty()) {
return R.fail("无法提取任何帧");
}
// ✅ 新增:保存帧为图片
//saveFramesToLocal(frames, "extracted_frame");
byte[] binaryData = convertFramesToRGB565(frames);
// String base64Data = Base64.getEncoder().encodeToString(binaryData);
//
// return R.ok(base64Data);
// 构造响应头
// 将二进制数据转为 Hex 字符串
// 转换为 Hex 字符串列表
List<String> hexList = bytesToHexList(binaryData);
return R.ok(hexList);
} catch (Exception e) {
return R.fail("视频处理失败:" + e.getMessage());
} finally {
deleteTempFile(tempFile);
}
}
/**
* rgb565 转 hex
*/
private List<String> bytesToHexList(byte[] bytes) {
List<String> hexList = new ArrayList<>();
for (byte b : bytes) {
int value = b & 0xFF;
char high = HEX_ARRAY[value >>> 4];
char low = HEX_ARRAY[value & 0x0F];
hexList.add(String.valueOf(high) + low);
}
return hexList;
}
/**
* 创建临时文件并保存上传的视频
*/
private File createTempVideoFile(MultipartFile file) throws Exception {
File tempFile = Files.createTempFile("upload-", ".mp4").toFile();
file.transferTo(tempFile);
return tempFile;
}
/**
* 从视频中按时间均匀提取指定数量的帧
*/
private List<BufferedImage> extractFramesFromVideo(File videoFile) throws Exception {
List<BufferedImage> frames = new ArrayList<>();
try (FFmpegFrameGrabber grabber = FFmpegFrameGrabber.createDefault(videoFile)) {
grabber.start();
// 获取视频总帧数和帧率
long totalFramesInVideo = grabber.getLengthInFrames();
int fps = (int) Math.round(grabber.getFrameRate());
if (fps <= 0) fps = 30;
double durationSeconds = (double) totalFramesInVideo / fps;
if (durationSeconds < DURATION) {
throw new IllegalArgumentException("视频太短,至少需要 " + DURATION + "");
}
// 计算每帧之间的间隔(浮点以实现更精确跳转)
double frameInterval = (double) totalFramesInVideo / TOTAL_FRAMES;
for (int i = 0; i < TOTAL_FRAMES; i++) {
int targetFrameNumber = (int) Math.round(i * frameInterval);
// 避免设置无效帧号
if (targetFrameNumber >= totalFramesInVideo) {
throw new IllegalArgumentException("目标帧超出范围: " + targetFrameNumber + " ");
}
grabber.setFrameNumber(targetFrameNumber);
Frame frame = grabber.grab();
if (frame != null && frame.image != null) {
BufferedImage bufferedImage = Java2DFrameUtils.toBufferedImage(frame);
frames.add(cropImage(bufferedImage, WIDTH, HEIGHT));
} else {
throw new IllegalArgumentException("无法获取第 " + targetFrameNumber + "");
}
}
grabber.stop();
}
return frames;
}
/**
* 将抽取的帧保存到本地,用于调试
*/
private void saveFramesToLocal(List<BufferedImage> frames, String prefix) {
// 指定输出目录
File outputDir = new File("output_frames");
if (!outputDir.exists()) {
outputDir.mkdirs();
}
int index = 0;
for (BufferedImage frame : frames) {
try {
File outputImage = new File(outputDir, prefix + "_" + (index++) + ".png");
ImageIO.write(frame, "png", outputImage);
System.out.println("保存帧图片成功: " + outputImage.getAbsolutePath());
} catch (Exception e) {
throw new IllegalArgumentException("保存帧图片失败 " + e);
}
}
}
/**
* 将所有帧转换为 RGB565 格式字节数组
*/
private byte[] convertFramesToRGB565(List<BufferedImage> frames) throws Exception {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
for (BufferedImage image : frames) {
byte[] rgb565Bytes = convertToRGB565(image);
byteArrayOutputStream.write(rgb565Bytes);
}
return byteArrayOutputStream.toByteArray();
}
/**
* 判断是否是支持的视频格式
*/
private boolean isVideo(String filename) {
String ext = filename.substring(filename.lastIndexOf('.')).toLowerCase();
return Arrays.asList(".mp4", ".avi", ".mov", ".mkv").contains(ext);
}
/**
* 裁剪图像到目标尺寸
*/
private BufferedImage cropImage(BufferedImage img, int targetWidth, int targetHeight) {
int w = Math.min(img.getWidth(), targetWidth);
int h = Math.min(img.getHeight(), targetHeight);
return img.getSubimage(0, 0, w, h);
}
/**
* 将 BufferedImage 转换为 RGB565 格式的字节数组
*/
private byte[] convertToRGB565(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
byte[] result = new byte[width * height * 2]; // RGB565: 2 bytes per pixel
int index = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int rgb = image.getRGB(x, y);
int r = ((rgb >> 16) & 0xFF) >> 3;
int g = ((rgb >> 8) & 0xFF) >> 2;
int b = (rgb & 0xFF) >> 3;
short pixel = (short) ((r << 11) | (g << 5) | b);
result[index++] = (byte) (pixel >> 8); // High byte first
result[index++] = (byte) pixel;
}
}
return result;
}
/**
* 删除临时文件
*/
private void deleteTempFile(File file) {
if (file != null && file.exists()) {
if (!file.delete()) {
throw new IllegalArgumentException("无法删除临时文件: " + file.getAbsolutePath());
}
}
}
}

View File

@ -6,17 +6,17 @@ import com.fuyuanshen.common.core.validate.EditGroup;
import com.fuyuanshen.common.idempotent.annotation.RepeatSubmit;
import com.fuyuanshen.common.log.annotation.Log;
import com.fuyuanshen.common.log.enums.BusinessType;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.web.core.BaseController;
import com.fuyuanshen.equipment.domain.bo.UserAppBo;
import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria;
import com.fuyuanshen.equipment.service.AppUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description:

View File

@ -4,11 +4,13 @@ package com.fuyuanshen.equipment.controller;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fuyuanshen.common.core.constant.ResponseMessageConstants;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.domain.ResponseVO;
import com.fuyuanshen.common.core.domain.model.LoginUser;
import com.fuyuanshen.common.core.utils.file.FileUtil;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.satoken.utils.LoginHelper;
import com.fuyuanshen.common.web.core.BaseController;
import com.fuyuanshen.customer.mapper.CustomerMapper;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.dto.DeviceExcelImportDTO;
@ -49,7 +51,7 @@ import java.util.List;
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/device")
public class DeviceController {
public class DeviceController extends BaseController {
private final ISysOssService ossService;
private final DeviceService deviceService;
@ -72,106 +74,106 @@ public class DeviceController {
// @Log("新增设备")
@Operation(summary = "新增设备")
@PostMapping(value = "/add")
public ResponseVO<Object> addDevice(@Validated @ModelAttribute DeviceForm deviceForm) {
public R<Void> addDevice(@Validated @ModelAttribute DeviceForm deviceForm) {
try {
deviceService.addDevice(deviceForm);
} catch (Exception e) {
log.error("addDevice error: " + e.getMessage());
return ResponseVO.fail(e.getMessage());
return R.fail(e.getMessage());
}
return ResponseVO.success(null);
return R.ok();
}
// @Log("修改设备")
@Operation(summary = "修改设备")
@PutMapping(value = "/update")
public ResponseVO<Object> updateDevice(@Validated @ModelAttribute DeviceForm deviceForm) {
public R<Void> updateDevice(@Validated @ModelAttribute DeviceForm deviceForm) {
try {
deviceService.update(deviceForm);
} catch (Exception e) {
e.printStackTrace();
log.error("updateDevice error: " + e.getMessage());
return ResponseVO.fail("出错了");
return R.fail(e.getMessage());
}
return ResponseVO.success(null);
return R.ok();
}
@Operation(summary = "设备详情")
@GetMapping(value = "/detail/{id}")
public ResponseVO<Object> getDevice(@PathVariable Long id) {
public R<Device> getDevice(@PathVariable Long id) {
Device device = deviceService.getById(id);
return ResponseVO.success(device);
return R.ok(device);
}
// @Log("删除设备")
@Operation(summary = "删除设备")
@DeleteMapping(value = "/delete")
public ResponseVO<Object> deleteDevice(@Parameter(name = "传ID数组[]") @RequestBody List<Long> ids) {
public R<Void> deleteDevice(@Parameter(name = "传ID数组[]") @RequestBody List<Long> ids) {
deviceService.deleteAll(ids);
return ResponseVO.success(ResponseMessageConstants.DELETE_SUCCESS);
return R.ok();
}
@Operation(summary = "设备定位")
@GetMapping(value = "/locateDevice")
public ResponseVO<Object> locateDevice(DeviceQueryCriteria criteria) throws IOException {
public R<List<Device>> locateDevice(DeviceQueryCriteria criteria) throws IOException {
List<Device> devices = deviceService.queryAllDevices(criteria);
return ResponseVO.success(devices);
return R.ok(devices);
}
// @Log("分配客户")
@Operation(summary = "分配客户")
@PutMapping(value = "/assignCustomer")
public ResponseVO<Object> assignCustomer(@Validated @RequestBody CustomerVo customerVo) {
public R<Void> assignCustomer(@Validated @RequestBody CustomerVo customerVo) {
deviceService.assignCustomer(customerVo);
return ResponseVO.success(null);
return R.ok();
}
// @Log("撤回设备")
@Operation(summary = "撤回分配设备")
@PostMapping(value = "/withdraw")
public ResponseVO<Object> withdrawDevice(@RequestBody List<Long> ids) {
public R<Void> withdrawDevice(@RequestBody List<Long> ids) {
try {
deviceService.withdrawDevice(ids);
} catch (Exception e) {
log.error("updateDevice error: " + e.getMessage());
return ResponseVO.fail("出错了");
return R.fail(e.getMessage());
}
return ResponseVO.success(null);
return R.ok();
}
/**
* @param deviceForm
* @param id
* @return
* @ModelAttribute 主要用于将请求参数绑定到 Java 对象上,它会从 HTTP 请求的查询参数Query Parameters
* 或表单数据Form Data中提取值并自动填充到指定的对象属性中。
*/
// @Log("解绑设备")
@Operation(summary = "解绑设备")
@PostMapping(value = "/unbind")
public ResponseVO<Object> unbindDevice(@Validated @ModelAttribute DeviceForm deviceForm) {
deviceService.unbindDevice(deviceForm);
return ResponseVO.success("解绑成功!!!");
@Operation(summary = "WEB端解绑设备")
@GetMapping(value = "/unbind")
public R<Void> unbindDevice(@Validated Long id) {
return toAjax(deviceService.webUnBindDevice(id));
}
@Operation(summary = "导出数据设备")
@GetMapping(value = "/download")
public void exportDevice(HttpServletResponse response, DeviceQueryCriteria criteria) throws IOException {
public R<Void> exportDevice(HttpServletResponse response, DeviceQueryCriteria criteria) throws IOException {
List<Device> devices = deviceService.queryAll(criteria);
exportService.export(devices, response);
return R.ok();
}
@Operation(summary = "导入设备数据")
@PostMapping(value = "/import", consumes = "multipart/form-data")
public ResponseVO<ImportResult> importData(@Parameter(name = "文件", required = true) @RequestPart("file") MultipartFile file) throws BadRequestException {
public R<ImportResult> importData(@Parameter(name = "文件", required = true) @RequestPart("file") MultipartFile file) throws BadRequestException {
String suffix = FileUtil.getExtensionName(file.getOriginalFilename());
if (!("xlsx".equalsIgnoreCase(suffix))) {
@ -195,13 +197,13 @@ public class DeviceController {
// 设置响应消息
String message = String.format("成功导入 %d 条数据,失败 %d 条", result.getSuccessCount(), result.getFailureCount());
// 返回带有正确泛型的响应
return ResponseVO.<ImportResult>success(message, result);
return R.ok(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);
return R.fail(errorMessage, result);
}
}

View File

@ -1,6 +1,7 @@
package com.fuyuanshen.equipment.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fuyuanshen.common.core.domain.R;
import com.fuyuanshen.common.core.domain.ResponseVO;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.equipment.domain.DeviceType;
@ -40,43 +41,45 @@ public class DeviceTypeController {
@GetMapping(value = "/all")
@Operation(summary = "查询所有设备类型")
public ResponseVO<Object> queryDeviceTypes() {
public R<List<DeviceType>> queryDeviceTypes() {
List<DeviceType> deviceTypes = deviceTypeService.queryDeviceTypes();
return ResponseVO.success(deviceTypes);
return R.ok(deviceTypes);
}
// @Log("新增设备类型")
@Operation(summary = "新增设备类型")
@PostMapping(value = "/add")
public ResponseVO<Object> createDeviceType(@Validated @RequestBody DeviceType resources) {
public R<Void> createDeviceType(@Validated @RequestBody DeviceType resources) {
deviceTypeService.create(resources);
return ResponseVO.success(null);
return R.ok();
}
// @Log("修改设备类型")
@Operation(summary = "修改设备类型")
@PutMapping(value = "/update")
public ResponseVO<Object> updateDeviceType(@Validated @RequestBody DeviceTypeForm resources) {
public R<Void> updateDeviceType(@Validated @RequestBody DeviceTypeForm resources) {
deviceTypeService.update(resources);
return ResponseVO.success(null);
return R.ok();
}
// @Log("删除设备类型")
@Operation(summary = "删除设备类型")
@DeleteMapping(value = "/delete")
public ResponseVO<Object> deleteDeviceType(@Parameter(name = "传ID数组[]") @RequestBody List<Long> ids) {
public R<Void> deleteDeviceType(@Parameter(name = "传ID数组[]") @RequestBody List<Long> ids) {
deviceTypeService.deleteAll(ids);
return ResponseVO.success("删除成功!!!");
return R.ok();
}
@GetMapping(value = "/communicationMode")
@Operation(summary = "获取设备类型通讯方式")
public ResponseVO<DeviceType> getCommunicationMode(@Parameter(name = "设备类型ID", required = true) Long id) {
return ResponseVO.success(deviceTypeService.getCommunicationMode(id));
public R<DeviceType> getCommunicationMode(@Parameter(name = "设备类型ID", required = true) Long id) {
DeviceType communicationMode = deviceTypeService.getCommunicationMode(id);
return R.ok(communicationMode);
}
}

View File

@ -9,6 +9,8 @@ import com.fuyuanshen.common.tenant.core.TenantEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
/**
* @Description: 设备表
* @Author: WY
@ -19,6 +21,9 @@ import lombok.Data;
@JsonInclude(JsonInclude.Include.ALWAYS) // 关键注解
public class Device extends TenantEntity {
/**
* id
*/
@TableId(value = "id", type = IdType.AUTO)
@Schema(name = "ID")
private Long id;
@ -27,6 +32,9 @@ public class Device extends TenantEntity {
@TableField(exist = false)
private Long assignId;
/**
* device_type
*/
@Schema(name = "设备类型")
private Long deviceType;
@ -98,8 +106,6 @@ public class Device extends TenantEntity {
private Integer deviceStatus;
/**
* 绑定状态
* 0 未绑定
@ -114,4 +120,9 @@ public class Device extends TenantEntity {
private String createByName;
private Long bindingUserId;
/**
* 绑定时间
*/
private Date bindingTime;
}

View File

@ -25,6 +25,7 @@ public class DeviceAssignments extends TenantEntity {
/**
* 设备id
* device_id
*/
private Long deviceId;
@ -34,7 +35,7 @@ public class DeviceAssignments extends TenantEntity {
private Long fromCustomerId;
/**
* 接收方
* 接收方(当前设备所处位置)
*/
private Long toCustomerId;

View File

@ -30,6 +30,11 @@ public class UserApp extends TenantEntity {
@TableId(value = "user_id")
private Long userId;
/**
* 部门ID
*/
private Long deptId;
/**
* 用户账号
*/
@ -42,6 +47,7 @@ public class UserApp extends TenantEntity {
/**
* 用户类型app_user系统用户
* 用户类型app_user手机端,xcx_user小程序
*/
private String userType;

View File

@ -58,7 +58,9 @@ public class DeviceExcelImportDTO {
private String remark;
@ExcelProperty("设备类型名称")
@ColumnWidth(20)
private String typeName;
@ExcelProperty("蓝牙名称")
private String bluetoothName;
}

View File

@ -1,10 +1,12 @@
package com.fuyuanshen.equipment.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class AppDeviceVo {
public class AppDeviceVo implements Serializable {
private Long id;
@ -39,7 +41,7 @@ public class AppDeviceVo {
private String typeName;
/**
* 蓝牙名称
* 蓝牙名称
*/
private String bluetoothName;
@ -49,4 +51,10 @@ public class AppDeviceVo {
* 1 正常
*/
private Integer deviceStatus;
/**
* 绑定时间
*/
private Date bindingTime;
}

View File

@ -0,0 +1,38 @@
package com.fuyuanshen.equipment.enums;
/**
* 用户类型枚举
*
* @author: 默苍璃
* @date: 2025-07-1210:31
*/
public enum AppUserTypeEnum {
APP_USER("app_user", "系统用户"),
XCX_USER("xcx_user", "小程序用户");
private final String code;
private final String description;
AppUserTypeEnum(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return "UserType{" +
"code='" + code + '\'' +
", description='" + description + '\'' +
'}';
}
}

View File

@ -2,6 +2,7 @@ package com.fuyuanshen.equipment.service;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.equipment.domain.UserApp;
import com.fuyuanshen.equipment.domain.bo.UserAppBo;
import java.util.Collection;
@ -23,5 +24,23 @@ public interface AppUserService {
*/
Boolean updateByBo(UserAppBo bo);
/**
* 查询小程序用户信息
*
* @param phoneNumber
* @return
*/
UserApp getMpUser(Long phoneNumber);
UserApp loadUserByUsername(String username);
/**
* 新增小程序用户信息
*
* @param userApp
* @return
*/
void saveMpUser(UserApp userApp);
}

View File

@ -85,10 +85,33 @@ public interface DeviceService extends IService<Device> {
*/
void unbindDevice(DeviceForm deviceForm);
/**
* WEB端查看APP客户设备绑定
*
* @param bo
* @param pageQuery
* @return
*/
TableDataInfo<AppDeviceVo> queryAppDeviceList(DeviceQueryCriteria bo, PageQuery pageQuery);
int bindDevice(AppDeviceBo bo);
int unBindDevice(Long id);
/**
* 检查设备MAC值是否存在
*
* @param mac
* @return
*/
Boolean queryDevice(String mac);
/**
* WEB端解绑设备
*
* @param id
* @return
*/
int webUnBindDevice(Long id);
}

View File

@ -10,6 +10,8 @@ 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.common.core.domain.model.LoginUser;
import com.fuyuanshen.common.core.exception.BadRequestException;
import com.fuyuanshen.common.core.utils.StringUtils;
import com.fuyuanshen.common.mybatis.core.page.PageQuery;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.satoken.utils.AppLoginHelper;
@ -54,6 +56,7 @@ import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
* @Description:
@ -97,6 +100,12 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
public TableDataInfo<Device> queryAll(DeviceQueryCriteria criteria, Page<Device> page) throws IOException {
criteria.setCurrentOwnerId(LoginHelper.getUserId());
if (criteria.getDeviceType() != null) {
DeviceTypeGrants deviceTypeGrant = deviceTypeGrantsMapper.selectById(criteria.getDeviceType());
if (deviceTypeGrant != null) {
criteria.setDeviceType(deviceTypeGrant.getDeviceTypeId());
}
}
IPage<Device> devices = deviceMapper.findAll(criteria, page);
List<Device> records = devices.getRecords();
@ -138,6 +147,16 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
@Override
@Transactional(rollbackFor = Exception.class)
public void addDevice(DeviceForm deviceForm) throws Exception {
Device device1 = deviceMapper.selectOne(new QueryWrapper<Device>().eq("device_mac", deviceForm.getDeviceMac()));
if (device1 != null) {
throw new BadRequestException("设备MAC已存在");
}
Device device2 = deviceMapper.selectOne(new QueryWrapper<Device>().eq("device_imei", deviceForm.getDeviceImei()));
if (device2 != null) {
throw new BadRequestException("设备IMEI已存在");
}
DeviceTypeQueryCriteria queryCriteria = new DeviceTypeQueryCriteria();
queryCriteria.setDeviceTypeId(deviceForm.getDeviceType());
queryCriteria.setCustomerId(LoginHelper.getUserId());
@ -272,20 +291,23 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
public void deleteAll(List<Long> ids) {
List<Long> invalidIds = new ArrayList<>();
for (Long id : ids) {
DeviceAssignments deviceAssignment = deviceAssignmentsMapper.selectById(id);
Device deviceType = deviceMapper.selectById(deviceAssignment.getDeviceId());
if (StringUtils.isNotEmpty(deviceAssignment.getAssigneeName())) {
throw new BadRequestException(deviceType.getDeviceName() + ":设备已分配,请先解绑设备!!!");
}
// 接收者
if (Objects.equals(deviceAssignment.getAssigneeId(), deviceType.getOriginalOwnerId())) {
invalidIds.add(deviceAssignment.getDeviceId());
}
}
deviceAssignmentsMapper.deleteByIds(ids);
//
// for (Long id : ids) {
//
// Device deviceType = deviceMapper.selectById(id);
// if (deviceType == null || !Objects.equals(deviceType.getCurrentOwnerId(), LoginHelper.getUserId())) {
// invalidIds.add(id);
// }
// }
// if (!invalidIds.isEmpty()) {
// throw new RuntimeException("以下设备无法删除ID 不存在或无权限): " + invalidIds);
// }
//
// deviceMapper.deleteByIds(ids);
deviceMapper.deleteByIds(invalidIds);
}
@ -313,9 +335,9 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
List<DeviceTypeGrants> deviceTypeGrants = new ArrayList<>();
for (DeviceAssignments assignment : assignments) {
if (assignment.getToCustomerId() != null) {
if (assignment.getToCustomerId() != null && assignment.getToCustomerId() != 0L) {
log.info("设备已经分配客户!!!");
continue;
throw new RuntimeException("设备已经分配客户!!!");
}
Device device = deviceMapper.selectById(assignment.getDeviceId());
@ -406,54 +428,6 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
}
/**
* 撤回设备
*
* @param ids
*/
// @Override
// public void withdrawDevice(List<Long> ids) {
// ids.forEach((id) -> {
// List<Device> deviceChain = getDeviceChain(id);
// deviceChain.forEach((device) -> {
// device.setDeviceStatus(DeviceStatusEnum.INVALID.getCode());
// deviceMapper.updateById(device);
// });
// });
//
// ids.forEach((id) -> {
// Device device = new Device();
// device.setId(id);
// device.setCustomerId(null);
// device.setCustomerName("");
// deviceMapper.updateById(device);
// });
//
// }
//
//
// public List<Device> getDeviceChain(Long originalDeviceId) {
// List<Device> chain = new ArrayList<>();
// Set<Long> visited = new HashSet<>(); // 防止循环引用
// findNext(chain, visited, originalDeviceId);
// return chain;
// }
//
// private void findNext(List<Device> chain, Set<Long> visited, Long currentOriginalDeviceId) {
// if (visited.contains(currentOriginalDeviceId)) {
// log.info("检测到循环引用,终止递归");
// return;
// }
// visited.add(currentOriginalDeviceId);
//
// List<Device> devices = deviceMapper.findByOriginalDeviceId(currentOriginalDeviceId);
// for (Device device : devices) {
// chain.add(device);
// findNext(chain, visited, device.getId());
// }
// }
/**
* 撤回设备
*
@ -468,7 +442,7 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
Device device = deviceMapper.selectById(assignment.getDeviceId());
// 接收者
assignment.setAssigneeName("");
assignment.setToCustomerId(null);
assignment.setToCustomerId(0L);
deviceAssignmentsMapper.updateById(assignment);
// 获取所有已分配的设备
@ -496,7 +470,13 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
}
/**
* WEB端查看APP客户设备绑定
*
* @param bo
* @param pageQuery
* @return
*/
@Override
public TableDataInfo<AppDeviceVo> queryAppDeviceList(DeviceQueryCriteria bo, PageQuery pageQuery) {
if (bo.getBindingUserId() == null) {
@ -527,8 +507,9 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
deviceUpdateWrapper.eq("id", device.getId())
.set("binding_status", BindingStatusEnum.BOUND.getCode())
.set("binding_user_id", userId);
;
.set("binding_user_id", userId)
.set("binding_time", new Date());
return baseMapper.update(null, deviceUpdateWrapper);
} else if (mode == CommunicationModeEnum.BLUETOOTH.getValue()) {
@ -546,7 +527,8 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
deviceUpdateWrapper.eq("id", device.getId())
.set("binding_status", BindingStatusEnum.BOUND.getCode())
.set("binding_user_id", userId);
.set("binding_user_id", userId)
.set("binding_time", new Date());
return baseMapper.update(null, deviceUpdateWrapper);
} else {
throw new RuntimeException("通讯方式错误");
@ -565,8 +547,44 @@ public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> impleme
UpdateWrapper<Device> deviceUpdateWrapper = new UpdateWrapper<>();
deviceUpdateWrapper.eq("id", device.getId())
.set("binding_status", BindingStatusEnum.UNBOUND.getCode())
.set("binding_user_id", null);
.set("binding_user_id", null)
.set("binding_time", null);
return baseMapper.update(null, deviceUpdateWrapper);
}
/**
* WEB端解绑设备
*
* @param id
* @return
*/
@Override
public int webUnBindDevice(Long id) {
DeviceAssignments deviceAssignment = deviceAssignmentsMapper.selectById(id);
if (deviceAssignment == null) {
throw new RuntimeException("请先将设备入库!!!");
}
return unBindDevice(deviceAssignment.getDeviceId());
}
/**
* 查询设备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

@ -1,6 +1,8 @@
package com.fuyuanshen.equipment.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
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;
@ -10,11 +12,13 @@ import com.fuyuanshen.common.core.utils.PageUtil;
import com.fuyuanshen.common.mybatis.core.page.TableDataInfo;
import com.fuyuanshen.common.satoken.utils.LoginHelper;
import com.fuyuanshen.equipment.domain.Device;
import com.fuyuanshen.equipment.domain.DeviceAssignments;
import com.fuyuanshen.equipment.domain.DeviceType;
import com.fuyuanshen.equipment.domain.DeviceTypeGrants;
import com.fuyuanshen.equipment.domain.form.DeviceTypeForm;
import com.fuyuanshen.equipment.domain.query.DeviceQueryCriteria;
import com.fuyuanshen.equipment.domain.query.DeviceTypeQueryCriteria;
import com.fuyuanshen.equipment.mapper.DeviceAssignmentsMapper;
import com.fuyuanshen.equipment.mapper.DeviceMapper;
import com.fuyuanshen.equipment.mapper.DeviceTypeGrantsMapper;
import com.fuyuanshen.equipment.mapper.DeviceTypeMapper;
@ -41,6 +45,7 @@ public class DeviceTypeServiceImpl extends ServiceImpl<DeviceTypeMapper, DeviceT
private final DeviceMapper deviceMapper;
private final DeviceTypeGrantsMapper deviceTypeGrantsMapper;
private final DeviceAssignmentsMapper deviceAssignmentsMapper;
/**
@ -155,30 +160,33 @@ public class DeviceTypeServiceImpl extends ServiceImpl<DeviceTypeMapper, DeviceT
@Transactional(rollbackFor = Exception.class)
public void deleteAll(List<Long> ids) {
List<Long> deviceTypeId = new ArrayList<>();
for (Long id : ids) {
DeviceTypeGrants deviceTypeGrant = deviceTypeGrantsMapper.selectById(id);
// List<DeviceTypeGrants> deviceTypeGrants = deviceTypeGrantsMapper.selectList(new QueryWrapper<DeviceTypeGrants>()
// .eq("device_type_id", deviceTypeGrant.getDeviceTypeId()));
// List<DeviceTypeGrants> deviceAssignments = deviceTypeGrantsMapper.selectList(new QueryWrapper<DeviceTypeGrants>()
// .eq("device_id", deviceTypeGrant.getDeviceTypeId()));
// if (CollectionUtil.isNotEmpty(deviceAssignments)) {
// throw new RuntimeException("该设备类型已绑定设备,无法删除");
// }
// Device device = deviceMapper.selectById(deviceTypeGrant.getDeviceTypeId());
List<Device> devices = deviceMapper.selectList(new QueryWrapper<Device>()
.eq("device_type", deviceTypeGrant.getDeviceTypeId()));
if (CollectionUtil.isNotEmpty(devices)) {
throw new RuntimeException("该设备类型已绑定设备,无法删除!!!");
}
deviceTypeId.add(deviceTypeGrant.getDeviceTypeId());
}
deviceTypeGrantsMapper.deleteByIds(ids);
//
// List<Long> invalidIds = new ArrayList<>();
// List<Long> invalidId2 = new ArrayList<>();
// for (Long id : ids) {
// DeviceType deviceType = deviceTypeMapper.selectById(id);
// if (deviceType == null || !Objects.equals(deviceType.getOwnerCustomerId(), LoginHelper.getUserId())) {
// invalidIds.add(id);
// }
// DeviceQueryCriteria deviceQueryCriteria = new DeviceQueryCriteria();
// deviceQueryCriteria.setDeviceType(id);
// List<Device> devices = deviceMapper.findAll(deviceQueryCriteria);
// if (!devices.isEmpty()) {
// invalidId2.add(id);
// }
// }
// if (!invalidIds.isEmpty()) {
// throw new RuntimeException("以下设备类型无法删除ID 不存在或无权限): " + invalidIds);
// }
// if (!invalidId2.isEmpty()) {
// throw new RuntimeException("以下设备类型无法删除(已绑定设备): " + invalidId2);
// }
//
// deviceTypeMapper.deleteByIds(ids);
deviceTypeMapper.deleteByIds(deviceTypeId);
}

View File

@ -1,8 +1,10 @@
package com.fuyuanshen.equipment.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fuyuanshen.common.core.utils.MapstructUtils;
import com.fuyuanshen.equipment.domain.UserApp;
import com.fuyuanshen.equipment.domain.bo.UserAppBo;
import com.fuyuanshen.equipment.enums.AppUserTypeEnum;
import com.fuyuanshen.equipment.mapper.UserAppMapper;
import com.fuyuanshen.equipment.service.AppUserService;
import lombok.RequiredArgsConstructor;
@ -22,7 +24,7 @@ import java.util.Collection;
@Service
public class UserAppServiceImpl implements AppUserService {
private final UserAppMapper baseMapper;
private final UserAppMapper userAppMapper;
/**
@ -34,7 +36,42 @@ public class UserAppServiceImpl implements AppUserService {
@Override
public Boolean updateByBo(UserAppBo bo) {
UserApp update = MapstructUtils.convert(bo, UserApp.class);
return baseMapper.updateById(update) > 0;
return userAppMapper.updateById(update) > 0;
}
/**
* 查询小程序用户信息
*
* @param phoneNumber
* @return
*/
@Override
public UserApp getMpUser(Long phoneNumber) {
QueryWrapper<UserApp> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("phonenumber", phoneNumber);
queryWrapper.eq("user_type", AppUserTypeEnum.XCX_USER.getCode());
return userAppMapper.selectOne(queryWrapper);
}
@Override
public UserApp loadUserByUsername(String username) {
QueryWrapper<UserApp> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_name", username);
queryWrapper.eq("user_type", AppUserTypeEnum.XCX_USER.getCode());
return userAppMapper.selectOne(queryWrapper);
}
/**
* 新增小程序用户信息
*
* @param userApp
* @return
*/
@Override
public void saveMpUser(UserApp userApp) {
userAppMapper.insert(userApp);
}

View File

@ -0,0 +1,239 @@
package com.fuyuanshen.equipment.utils.c;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 可靠点阵生成工具 - 终极版
*
* @author: 默苍璃
* @date: 2025-07-15
*/
public class ReliableTextToBitmap {
public static void main(String[] args) throws IOException {
String unit = "繁體繁體繁體";
String department = "A研B发C";
String name = "12李34四56";
byte[] unitBytes = textToBitmapBytes(unit);
byte[] deptBytes = textToBitmapBytes(department);
byte[] nameBytes = textToBitmapBytes(name);
generateCFile(unitBytes, deptBytes, nameBytes, "display_data.c");
// 生成预览图片
generatePreviewImage(unitBytes, "unit_image.png");
generatePreviewImage(deptBytes, "department_image.png");
generatePreviewImage(nameBytes, "name_image.png");
}
// 专门生成预览图片的方法
private static void generatePreviewImage(byte[] data, String filename) {
try {
BufferedImage image = convertByteArrayToImage(data, 12);
ImageIO.write(image, "PNG", new File(filename));
System.out.println("成功生成预览图片: " + filename);
} catch (IOException e) {
System.err.println("图片生成失败: " + e.getMessage());
}
}
/**
* 可靠的字节数组转图像方法
*/
public static BufferedImage convertByteArrayToImage(byte[] data, int height) {
if (data == null || data.length == 0) {
return new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
}
// 计算宽度
int width = (data.length * 8) / height;
// 创建RGB图像
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 设置白色背景
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
image.setRGB(x, y, Color.WHITE.getRGB());
}
}
// 直接设置像素点
int bitIndex = 0;
for (int i = 0; i < data.length; i++) {
int value = data[i] & 0xFF;
for (int bit = 7; bit >= 0; bit--) { // 高位在前
boolean isBlack = ((value >> bit) & 1) == 1;
if (isBlack) {
int x = bitIndex % width;
int y = bitIndex / width;
if (y < height) { // 确保不越界
image.setRGB(x, y, Color.BLACK.getRGB());
}
}
bitIndex++;
}
}
return image;
}
/**
* 可靠的点阵生成方法
*/
public static byte[] textToBitmapBytes(String text) {
if (text == null || text.isEmpty()) {
return new byte[0];
}
// 1. 创建画布 - 使用RGB类型避免问题
Font font = new Font("宋体", Font.PLAIN, 12);
BufferedImage image = createTextImage(text, font);
// 2. 直接提取点阵数据
return extractBitmapData(image);
}
/**
* 创建文本图像 - 最可靠的方法
*/
private static BufferedImage createTextImage(String text, Font font) {
// 创建临时图像获取字体度量
BufferedImage tempImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
Graphics2D tempG = tempImage.createGraphics();
tempG.setFont(font);
FontMetrics metrics = tempG.getFontMetrics();
// 计算文本宽度
int totalWidth = metrics.stringWidth(text);
// 宽度对齐到8的倍数
int width = (int) Math.ceil(totalWidth / 8.0) * 8;
int height = 12;
// 创建RGB图像
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
// 设置白色背景
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
// 设置黑色文本
g.setColor(Color.BLACK);
g.setFont(font);
// 关闭抗锯齿 - 确保清晰度
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
// 绘制文本 - 精确居中
int y = (height - metrics.getHeight()) / 2 + metrics.getAscent();
g.drawString(text, 0, y);
g.dispose();
tempG.dispose();
return image;
}
/**
* 提取点阵数据 - 简单可靠的方法
*/
private static byte[] extractBitmapData(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
List<Byte> byteList = new ArrayList<>();
int currentByte = 0;
int bitCount = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// 获取像素颜色
Color color = new Color(image.getRGB(x, y));
// 简单阈值判断 - 灰度小于128为黑色
int gray = (color.getRed() + color.getGreen() + color.getBlue()) / 3;
boolean isBlack = gray < 128;
// 高位优先打包
currentByte = (currentByte << 1) | (isBlack ? 1 : 0);
bitCount++;
if (bitCount == 8) {
byteList.add((byte) currentByte);
currentByte = 0;
bitCount = 0;
}
}
}
// 处理最后不满8位的部分
if (bitCount > 0) {
currentByte <<= (8 - bitCount);
byteList.add((byte) currentByte);
}
return byteListToArray(byteList);
}
private static byte[] byteListToArray(List<Byte> byteList) {
byte[] result = new byte[byteList.size()];
for (int i = 0; i < byteList.size(); i++) {
result[i] = byteList.get(i);
}
return result;
}
public static void generateCFile(byte[] unit, byte[] department, byte[] name, String filename) throws IOException {
try (FileWriter writer = new FileWriter(filename)) {
writer.write("/**\n");
writer.write(" * 点阵显示数据 - 可靠生成\n");
writer.write(" * 宽度计算公式: width = (数组长度 * 8) / 12\n");
writer.write(" */\n\n");
writer.write("#include <stdint.h>\n\n");
// 单位数据
writer.write(String.format("// 单位: %d 字节, 宽度: %d 像素\n",
unit.length, (unit.length * 8) / 12));
writer.write("const uint8_t unit[] = {\n ");
writeByteArray(writer, unit);
writer.write("\n};\n\n");
// 部门数据
writer.write(String.format("// 部门: %d 字节, 宽度: %d 像素\n",
department.length, (department.length * 8) / 12));
writer.write("const uint8_t department[] = {\n ");
writeByteArray(writer, department);
writer.write("\n};\n\n");
// 姓名数据
writer.write(String.format("// 姓名: %d 字节, 宽度: %d 像素\n",
name.length, (name.length * 8) / 12));
writer.write("const uint8_t name[] = {\n ");
writeByteArray(writer, name);
writer.write("\n};\n");
}
}
private static void writeByteArray(FileWriter writer, byte[] data) throws IOException {
for (int i = 0; i < data.length; i++) {
int value = data[i] & 0xFF;
writer.write(String.format("0x%02X", value));
if (i < data.length - 1) {
writer.write(", ");
if ((i + 1) % 12 == 0) writer.write("\n ");
}
}
}
}

View File

@ -36,48 +36,12 @@
d.device_imei, d.longitude, d.latitude, d.tenant_id
</sql>
<!-- 分页查询设备 -->
<select id="findAll1" resultType="com.fuyuanshen.equipment.domain.Device">
select d.* , t.type_name
FROM device d
LEFT JOIN device_type t ON d.device_type = t.id
<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 d.device_mac = #{criteria.deviceMac}
</if>
<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}
</if>
<if test="criteria.currentOwnerId != null">
and d.current_owner_id = #{criteria.currentOwnerId}
</if>
<if test="criteria.params.beginTime != null and criteria.params.endTime != null">
and d.create_time between #{criteria.params.beginTime} and #{criteria.params.endTime}
</if>
<if test="criteria.tenantId != null">
AND tenant_id = #{criteria.tenantId}
</if>
</where>
order by d.create_time desc
</select>
<!-- 分页查询设备 -->
<select id="findAll" resultType="com.fuyuanshen.equipment.domain.Device">
select
da.id AS id,d.device_name,d.bluetooth_name,
d.device_pic, d.device_mac, d.device_sn, d.update_by,d.device_imei,
d.update_time, dg.id AS device_type, d.remark, d.binding_status,d.type_name AS typeName,
d.update_time, dg.id AS device_type, d.remark, d.binding_status,t.type_name AS typeName,
da.assignee_id AS customerId, da.assignee_name AS customerName, da.active AS deviceStatus,
da.create_time AS create_time , da.assigner_name AS createByName , da.id AS assignId
from device d
@ -166,7 +130,7 @@
<!-- 查询APP绑定设备列表 -->
<select id="queryAppBindDeviceList" resultType="com.fuyuanshen.equipment.domain.vo.AppDeviceVo">
select d.id, d.device_name, d.device_name,
select d.id, d.device_name, d.device_name,
d.device_name,
d.device_mac,
d.device_sn,
@ -174,7 +138,8 @@
d.device_pic,
dt.type_name,
dt.communication_mode,
d.bluetooth_name
d.bluetooth_name,
d.binding_time
from device d
inner join device_type dt on d.device_type = dt.id
where d.binding_user_id = #{criteria.bindingUserId}

View File

@ -37,6 +37,8 @@
<lombok.version>1.18.36</lombok.version>
<bouncycastle.version>1.80</bouncycastle.version>
<justauth.version>1.16.7</justauth.version>
<javacv-platform.version>1.5.7</javacv-platform.version>
<!-- 离线IP地址定位库 -->
<ip2region.version>2.7.0</ip2region.version>
@ -142,6 +144,12 @@
<version>${justauth.version}</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>${javacv-platform.version}</version> <!-- 使用合适的版本号 -->
</dependency>
<!-- common 的依赖配置-->
<dependency>
<groupId>com.fuyuanshen</groupId>