1.添加微信小程序验证登录

2.添加敏感词上传
3.保存工作描述时,验证敏感词
This commit is contained in:
sh
2025-10-20 09:54:51 +08:00
parent da48a9c33a
commit 47351f41eb
20 changed files with 681 additions and 0 deletions

View File

@@ -2,6 +2,9 @@ package com.ruoyi.web.controller.system;
import java.util.List;
import java.util.Set;
import com.ruoyi.common.core.domain.model.LoginSiteUser;
import com.ruoyi.common.utils.SiteSecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.constant.Constants;
@@ -56,6 +59,21 @@ public class SysLoginController
ajax.put(Constants.TOKEN, token);
return ajax;
}
@PostMapping("/app/appLogin")
public AjaxResult appLogin(@RequestBody LoginBody loginBody)
{
AjaxResult ajax = AjaxResult.success();
// 若携带令牌且有效,直接返回
if (SiteSecurityUtils.isLogin()) {
LoginSiteUser loginSiteUser = SiteSecurityUtils.getLoginUser();
ajax.put(Constants.TOKEN, loginSiteUser.getToken());
}else{
ajax=loginService.appLogin(loginBody);
}
return ajax;
}
/**
* 获取用户信息
*

View File

@@ -116,4 +116,12 @@ public class AppUserController extends BaseController
HashMap<String,Integer> result = jobApplyService.statistics();
return AjaxResult.success(result);
}
@ApiOperation("根据条件查询用户信息")
@GetMapping("/list")
public AjaxResult getUserList(AppUser appUser)
{
List<AppUser> list = appUserService.selectAppUserList(appUser);
return AjaxResult.success(list);
}
}

View File

@@ -3,6 +3,7 @@ package com.ruoyi.cms.controller.cms;
import com.ruoyi.cms.domain.Job;
import com.ruoyi.cms.domain.vo.CandidateVO;
import com.ruoyi.cms.service.IJobService;
import com.ruoyi.cms.util.sensitiveWord.SensitiveWordChecker;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
@@ -33,6 +34,8 @@ public class CmsJobController extends BaseController
{
@Autowired
private IJobService jobService;
@Autowired
SensitiveWordChecker sensitiveWordChecker;
/**
* 查询岗位列表
*/
@@ -79,6 +82,13 @@ public class CmsJobController extends BaseController
@PostMapping
public AjaxResult add(@RequestBody Job job)
{
// 校验描述中的敏感词
List<String> sensitiveWords = sensitiveWordChecker.checkSensitiveWords(job.getDescription());
if (!sensitiveWords.isEmpty()) {
String errorMsg = "描述中包含敏感词:" + String.join("", sensitiveWords);
return AjaxResult.error(errorMsg);
}
// 无敏感词,执行插入
return toAjax(jobService.insertJob(job));
}
@@ -91,6 +101,12 @@ public class CmsJobController extends BaseController
@PutMapping
public AjaxResult edit(@RequestBody Job job)
{
// 校验描述中的敏感词
List<String> sensitiveWords = sensitiveWordChecker.checkSensitiveWords(job.getDescription());
if (!sensitiveWords.isEmpty()) {
String errorMsg = "描述中包含敏感词:" + String.join("", sensitiveWords);
return AjaxResult.error(errorMsg);
}
return toAjax(jobService.updateJob(job));
}

View File

@@ -2,6 +2,7 @@ package com.ruoyi.cms.controller.cms;
import com.ruoyi.cms.domain.SensitiveWordData;
import com.ruoyi.cms.service.SensitiveWordDataService;
import com.ruoyi.cms.util.EasyExcelUtils;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
@@ -12,7 +13,9 @@ import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.List;
@@ -88,4 +91,24 @@ public class SensitiveWordDataController extends BaseController {
return toAjax(sensitiveWordDataService.deleteSensitiveworddataIds(ids));
}
/**
* 通用上传请求(单个)
*/
@PostMapping("/exoprt")
public AjaxResult uploadFile(@RequestParam("file") MultipartFile file) throws Exception
{
try
{
InputStream inputStream = file.getInputStream();
EasyExcelUtils.readExcelByBatch(inputStream, SensitiveWordData.class, 100, list -> {
// 处理逻辑:如批量保存到数据库
sensitiveWordDataService.batchInsert(list);
});
}
catch (Exception e)
{
return AjaxResult.error(e.getMessage());
}
return AjaxResult.success();
}
}

View File

@@ -105,4 +105,10 @@ public class Company extends BaseEntity
@TableField(exist = false)
@ApiModelProperty("驳回时间")
private String rejectTime;
@ApiModelProperty("是否本地重点企业")
private String isImpCompany;
@ApiModelProperty("本地重点发展产业")
private String impCompanyType;
}

View File

@@ -1,5 +1,6 @@
package com.ruoyi.cms.domain;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
@@ -31,11 +32,13 @@ public class SensitiveWordData extends BaseEntity {
/**
* 敏感词
*/
@ExcelProperty(value = "敏感词", index = 0)
@ApiModelProperty("敏感词")
private String sensitiveWord;
/**
* 类型
*/
@ExcelProperty(value = "类型", index = 1)
@ApiModelProperty("类型")
private String type;

View File

@@ -16,4 +16,6 @@ import java.util.List;
public interface SensitiveWordDataMapper extends BaseMapper<SensitiveWordData> {
List<SensitiveWordData> selectSensitiveworddataList(SensitiveWordData sensitiveWordData);
int batchInsert(List<SensitiveWordData> list);
}

View File

@@ -51,4 +51,5 @@ public interface IAppUserService
*/
public int deleteAppUserByUserIds(Long[] userIds);
public AppUser getPhone(String phone);
}

View File

@@ -22,5 +22,7 @@ public interface SensitiveWordDataService {
int updateSensitiveworddata(SensitiveWordData sensitiveWordData);
int deleteSensitiveworddataIds(Long[] ids);
int batchInsert(List<SensitiveWordData> list);
}

View File

@@ -4,6 +4,7 @@ import java.util.List;
import java.util.Arrays;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.cms.mapper.JobTitleMapper;
import com.ruoyi.cms.util.StringUtil;
import com.ruoyi.common.core.domain.entity.JobTitle;
@@ -102,4 +103,9 @@ public class AppUserServiceImpl extends ServiceImpl<AppUserMapper,AppUser> imple
return appUserMapper.deleteBatchIds(Arrays.asList(userIds));
}
@Override
public AppUser getPhone(String phone) {
return appUserMapper.selectOne(new LambdaQueryWrapper<AppUser>()
.eq(AppUser::getPhone, phone));
}
}

View File

@@ -40,4 +40,9 @@ public class SensitiveWordDataServiceImpl implements SensitiveWordDataService {
public int deleteSensitiveworddataIds(Long[] ids) {
return sensitiveWordDataMapper.deleteBatchIds(Arrays.asList(ids));
}
@Override
public int batchInsert(List<SensitiveWordData> list) {
return sensitiveWordDataMapper.batchInsert(list);
}
}

View File

@@ -0,0 +1,198 @@
package com.ruoyi.cms.util;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
/**
* EasyExcel 工具类(基于 alibaba easyexcel 3.x+
*/
public class EasyExcelUtils {
/**
* 读取 Excel 文件(一次性读取所有数据)
*
* @param file 上传的 Excel 文件
* @param head 实体类字节码(需使用 @ExcelProperty 注解映射表头)
* @param <T> 实体类泛型
* @return 解析后的数据集
*/
public static <T> List<T> readExcel(File file, Class<T> head) {
return EasyExcel.read(file)
.head(head)
.sheet()
.doReadSync();
}
/**
* 读取 Excel 输入流(一次性读取所有数据)
*
* @param inputStream Excel 输入流(如 MultipartFile 的 getInputStream()
* @param head 实体类字节码
* @param <T> 实体类泛型
* @return 解析后的数据集
*/
public static <T> List<T> readExcel(InputStream inputStream, Class<T> head) {
return EasyExcel.read(inputStream)
.head(head)
.sheet()
.doReadSync();
}
/**
* 分批读取 Excel适用于大数据量避免内存溢出
*
* @param inputStream Excel 输入流
* @param head 实体类字节码
* @param batchSize 每批处理的数据量
* @param consumer 数据处理函数(如批量保存到数据库)
* @param <T> 实体类泛型
*/
public static <T> void readExcelByBatch(InputStream inputStream, Class<T> head, int batchSize, Consumer<List<T>> consumer) {
EasyExcel.read(inputStream)
.head(head)
.sheet()
.registerReadListener(new AnalysisEventListener<T>() {
private List<T> batchList; // 临时存储批数据
@Override
public void invoke(T data, AnalysisContext context) {
if (batchList == null) {
batchList = new java.util.ArrayList<>(batchSize);
}
batchList.add(data);
// 达到批处理量时执行消费逻辑
if (batchList.size() >= batchSize) {
consumer.accept(batchList);
batchList.clear(); // 清空集合,释放内存
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 处理剩余不足一批的数据
if (batchList != null && !batchList.isEmpty()) {
consumer.accept(batchList);
}
}
})
.doRead();
}
/**
* 生成 Excel 并写入到输出流(通用样式)
*
* @param outputStream 输出流(如 HttpServletResponse 的 getOutputStream()
* @param data 数据集
* @param head 实体类字节码
* @param sheetName 工作表名称
* @param <T> 实体类泛型
*/
public static <T> void writeExcel(OutputStream outputStream, List<T> data, Class<T> head, String sheetName) {
// 构建通用样式策略(表头居中加粗,内容居中)
HorizontalCellStyleStrategy styleStrategy = getDefaultStyleStrategy();
ExcelWriterSheetBuilder writerBuilder = EasyExcel.write(outputStream, head)
.sheet(sheetName)
.registerWriteHandler(styleStrategy);
writerBuilder.doWrite(data);
}
/**
* 生成 Excel 并通过 HttpServletResponse 下载(前端直接触发下载)
*
* @param response HttpServletResponse
* @param data 数据集
* @param head 实体类字节码
* @param sheetName 工作表名称
* @param fileName 下载的文件名(不带后缀)
* @param <T> 实体类泛型
* @throws IOException IO异常
*/
public static <T> void downloadExcel(HttpServletResponse response, List<T> data, Class<T> head,
String sheetName, String fileName) throws IOException {
// 设置响应头,触发前端下载
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("UTF-8");
// 文件名编码,避免中文乱码
String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=UTF-8''" + encodedFileName + ".xlsx");
// 写入数据到响应流
writeExcel(response.getOutputStream(), data, head, sheetName);
}
/**
* 填充 Excel 模板(适用于带固定格式的模板文件)
*
* @param templateInputStream 模板文件输入流
* @param outputStream 输出流(如响应流或文件流)
* @param dataMap 填充数据key为模板中的占位符value为填充值
* @param sheetName 工作表名称
*/
public static void fillTemplate(InputStream templateInputStream, OutputStream outputStream,
Map<String, Object> dataMap, String sheetName) {
EasyExcel.write(outputStream)
.withTemplate(templateInputStream)
.sheet(sheetName)
.doFill(dataMap);
}
/**
* 获取默认单元格样式策略(表头加粗居中,内容居中)
*/
private static HorizontalCellStyleStrategy getDefaultStyleStrategy() {
// 表头样式
WriteCellStyle headStyle = new WriteCellStyle();
WriteFont headFont = new WriteFont();
headFont.setBold(true); // 加粗
headFont.setFontHeightInPoints((short) 11);
headStyle.setWriteFont(headFont);
headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); // 水平居中
headStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中
// 内容样式
WriteCellStyle contentStyle = new WriteCellStyle();
WriteFont contentFont = new WriteFont();
contentFont.setFontHeightInPoints((short) 11);
contentStyle.setWriteFont(contentFont);
contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
contentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
// 返回样式策略
return new HorizontalCellStyleStrategy(headStyle, contentStyle);
}
/**
* 关闭流(工具类内部使用)
*/
private static void closeStream(Closeable... closeables) {
if (closeables != null) {
for (Closeable closeable : closeables) {
if (Objects.nonNull(closeable)) {
try {
closeable.close();
} catch (IOException e) {
// 日志记录(建议替换为实际项目的日志框架)
e.printStackTrace();
}
}
}
}
}
}

View File

@@ -11,10 +11,14 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
@@ -210,6 +214,60 @@ public class WechatUtil {
return result;
}
/**
* 通过code获取微信用户的openid和session_key
*
* @param appid 小程序appid
* @param secret 小程序secret
* @param code 登录凭证code
* @return 包含openid、session_key、unionid的JSON对象
*/
public JSONObject code2Session(String appid, String secret, String code) {
try {
String response = getAccessTokenData("https://api.weixin.qq.com/sns/jscode2session?appid="+appid+"&secret="+secret+"&js_code="+code+"&grant_type=authorization_code");
JSONObject result = JSONObject.parseObject(response);
// 微信返回错误码处理
if (result.containsKey("errcode") && result.getInteger("errcode") != 0) {
throw new RuntimeException("微信授权失败:" + result.getString("errmsg"));
}
return result;
} catch (Exception e) {
throw new RuntimeException("调用微信接口失败:" + e.getMessage());
}
}
/**
* 解密微信用户手机号(用户通过 getPhoneNumber 组件授权后返回的加密数据)
* @param encryptedData 微信返回的加密手机号数据
* @param sessionKey 从 code2Session 接口获取的会话密钥
* @param iv 微信返回的加密向量(与 encryptedData 配套)
* @return 解密后的 JSON 对象(包含 phoneNumber、purePhoneNumber 等字段)
* @throws RuntimeException 解密失败时抛出
*/
public JSONObject decryptPhoneNumber(String encryptedData, String sessionKey, String iv) {
try {
// 1. Base64 解码encryptedData、sessionKey、iv 均为 Base64 编码)
byte[] encryptedDataBytes = Base64.getDecoder().decode(encryptedData);
byte[] sessionKeyBytes = Base64.getDecoder().decode(sessionKey);
byte[] ivBytes = Base64.getDecoder().decode(iv);
// 2. 初始化 AES-128-CBC 解密器(微信固定加密算法)
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
SecretKeySpec keySpec = new SecretKeySpec(sessionKeyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
// 3. 执行解密并转换为字符串
byte[] decryptedBytes = cipher.doFinal(encryptedDataBytes);
String decryptedStr = new String(decryptedBytes, StandardCharsets.UTF_8);
// 4. 解析为 JSON 并返回(包含手机号等信息)
return JSONObject.parseObject(decryptedStr);
} catch (Exception e) {
throw new RuntimeException("解密用户手机号失败:" + e.getMessage(), e);
}
}
private String create_nonce_str() {
return IdUtil.simpleUUID();
}

View File

@@ -0,0 +1,105 @@
package com.ruoyi.cms.util.sensitiveWord;
import com.ruoyi.cms.domain.SensitiveWordData;
import com.ruoyi.cms.mapper.SensitiveWordDataMapper;
import com.ruoyi.common.core.redis.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Component
public class SensitiveWordChecker {
@Autowired
private SensitiveWordDataMapper sensitiveWordDataMapper;
@Autowired
private SensitiveWordTrie trie; // 注入前缀树
// 缓存键常量
private static final String SENSITIVE_WORD_CACHE_KEY = "sensitive:words";
@Autowired
private RedisCache redisCache;
/**
* 项目启动时初始化敏感词库到前缀树
*/
@PostConstruct
public void init() {
List<String> sensitiveWords = getSensitiveWordsFromCache();
if (sensitiveWords.isEmpty()) {
// 缓存未命中,从数据库加载
sensitiveWords = loadSensitiveWordsFromDbAndCache();
}
// 初始化前缀树
trie.batchAddWords(sensitiveWords);
}
/**
* 检测文本中的敏感词
* @param text 待检测文本如job.getDescription()
* @return 敏感词列表(空列表表示无敏感词)
*/
public List<String> checkSensitiveWords(String text) {
return trie.findAllSensitiveWords(text);
}
/**
* 从缓存获取敏感词列表
*/
@Cacheable(value = SENSITIVE_WORD_CACHE_KEY)
public List<String> getSensitiveWordsFromCache() {
// 从Redis获取
Object cachedWords = redisCache.getCacheObject(SENSITIVE_WORD_CACHE_KEY);
if (cachedWords instanceof List<?>) {
return ((List<?>) cachedWords).stream()
.filter(obj -> obj instanceof String)
.map(obj -> (String) obj)
.collect(Collectors.toList());
}
return Collections.emptyList();
}
/**
* 从数据库加载敏感词并更新缓存
*/
public List<String> loadSensitiveWordsFromDbAndCache() {
List<SensitiveWordData> wordList = sensitiveWordDataMapper.selectSensitiveworddataList(new SensitiveWordData());
List<String> sensitiveWords = wordList.stream()
.map(SensitiveWordData::getSensitiveWord)
.collect(Collectors.toList());
// 缓存有效期设置为24小时可根据实际需求调整
redisCache.setCacheObject(SENSITIVE_WORD_CACHE_KEY, sensitiveWords, 24, TimeUnit.HOURS);
return sensitiveWords;
}
/**
* 敏感词更新时清除缓存(需在敏感词管理服务中调用)
*/
@CacheEvict(value = SENSITIVE_WORD_CACHE_KEY, allEntries = true)
public void clearSensitiveWordCache() {
// 清除缓存后可重新加载
loadSensitiveWordsFromDbAndCache();
// 重新初始化前缀树
trie.clear();
init();
}
/**
* 新增/修改敏感词后更新缓存和前缀树
*/
public void updateCacheAfterModify() {
// 1. 清除旧缓存
redisCache.deleteObject(SENSITIVE_WORD_CACHE_KEY);
// 2. 从数据库重新加载最新数据并更新缓存
List<String> latestWords = loadSensitiveWordsFromDbAndCache();
// 3. 重建前缀树
trie.clear();
trie.batchAddWords(latestWords);
}
}

View File

@@ -0,0 +1,97 @@
package com.ruoyi.cms.util.sensitiveWord;
import org.springframework.stereotype.Component;
import java.util.*;
/**
* 前缀树Trie树节点
*/
/**
* 前缀树Trie树工具类
*/
@Component
public class SensitiveWordTrie {
// 根节点(无实际字符)
private final TrieNode root = new TrieNode();
/**
* 向前缀树中添加敏感词
*/
public void addWord(String word) {
if (word == null || word.isEmpty()) {
return;
}
TrieNode current = root;
for (char c : word.toCharArray()) {
// 若子节点中不存在该字符,则创建新节点
current.getChildren().putIfAbsent(c, new TrieNode());
// 移动到子节点
current = current.getChildren().get(c);
}
// 标记当前节点为敏感词结尾
current.setEnd(true);
}
/**
* 检测文本中所有敏感词(去重)
* @param text 待检测文本
* @return 敏感词列表
*/
public List<String> findAllSensitiveWords(String text) {
if (text == null || text.isEmpty()) {
return new ArrayList<>();
}
Set<String> sensitiveWords = new HashSet<>();
TrieNode current = root;
StringBuilder currentWord = new StringBuilder(); // 记录当前匹配的字符
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
// 若当前字符不在子节点中,重置匹配状态
if (!current.getChildren().containsKey(c)) {
current = root; // 回到根节点
currentWord.setLength(0); // 清空当前匹配的字符
continue;
}
// 匹配到字符,移动到子节点
current = current.getChildren().get(c);
currentWord.append(c);
// 若当前节点是敏感词结尾,记录该敏感词
if (current.isEnd()) {
sensitiveWords.add(currentWord.toString());
}
}
return new ArrayList<>(sensitiveWords);
}
/**
* 批量添加敏感词到前缀树
* @param words 敏感词列表
*/
public void batchAddWords(List<String> words) {
if (words == null || words.isEmpty()) {
return;
}
// 批量处理比循环单次添加更高效,减少重复判断
for (String word : words) {
addWord(word);
}
}
/**
* 清空前缀树所有节点
*/
public void clear() {
// 清空根节点的子节点映射,达到清空整个前缀树的效果
root.getChildren().clear();
}
}

View File

@@ -0,0 +1,23 @@
package com.ruoyi.cms.util.sensitiveWord;
import java.util.HashMap;
import java.util.Map;
class TrieNode {
// 子节点key=字符value=子节点
private final Map<Character, TrieNode> children = new HashMap<>();
// 标记当前节点是否为敏感词的结尾
private boolean isEnd = false;
public Map<Character, TrieNode> getChildren() {
return children;
}
public boolean isEnd() {
return isEnd;
}
public void setEnd(boolean end) {
isEnd = end;
}
}

View File

@@ -29,4 +29,12 @@
</where>
</select>
<insert id="batchInsert" parameterType="java.util.List">
INSERT INTO sensitive_word_data (sensitive_word, type, create_time,create_by,del_flag,remark)
VALUES
<foreach collection="list" item="item" separator=",">
(#{item.sensitiveWord}, #{item.type}, #{item.createTime},#{item.createBy},#{item.delFlag},#{item.remark})
</foreach>
</insert>
</mapper>

View File

@@ -113,4 +113,15 @@ public class AppUser extends BaseEntity
@TableField(exist = false)
@ApiModelProperty("密码")
private String password;
/**
* 微信小程序用户唯一标识
*/
@ApiModelProperty("微信小程序用户唯一标识")
private String openid;
/**
* 微信开放平台全局标识
*/
@ApiModelProperty("微信开放平台全局标识")
private String unionid;
}

View File

@@ -26,6 +26,14 @@ public class LoginBody
* 唯一标识
*/
private String uuid;
/**
* 微信加密的手机号数据
*/
private String encryptedData;
/**
* 加密向量
*/
private String iv;
public String getUsername()
{
@@ -66,4 +74,20 @@ public class LoginBody
{
this.uuid = uuid;
}
public String getEncryptedData() {
return encryptedData;
}
public void setEncryptedData(String encryptedData) {
this.encryptedData = encryptedData;
}
public String getIv() {
return iv;
}
public void setIv(String iv) {
this.iv = iv;
}
}

View File

@@ -2,7 +2,12 @@ package com.ruoyi.framework.web.service;
import javax.annotation.Resource;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.cms.service.IAppUserService;
import com.ruoyi.cms.util.WechatUtil;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.AppUser;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.core.domain.model.LoginSiteUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
@@ -57,6 +62,10 @@ public class SysLoginService
@Autowired
private TokenSiteService tokenSiteService;
@Autowired
WechatUtil wechatUtil;
@Autowired
private IAppUserService appUserService;
/**
* 登录验证
*
@@ -118,6 +127,24 @@ public class SysLoginService
// 生成token
return tokenSiteService.createToken(loginSiteUser);
}
/**
* 根据微信生成的
* @param appUser
* @return
*/
public String loginUserIdApp(AppUser appUser)
{
LoginSiteUser loginSiteUser = new LoginSiteUser();
loginSiteUser.setUserId(appUser.getUserId());
loginSiteUser.setUser(appUser);
recordLoginInfo(appUser);
AsyncManager.me().execute(AsyncFactory.recordLogininfor(appUser.getName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
recordLoginInfo(appUser);
// 生成token
return tokenSiteService.createToken(loginSiteUser);
}
//单点登录
public String loginOss(String ticket)
{
@@ -209,4 +236,44 @@ public class SysLoginService
sysUser.setLoginDate(DateUtils.getNowDate());
userService.updateUserProfile(sysUser);
}
/**
* 小程序微信授权登录
* @param dto
* @return
*/
public AjaxResult appLogin(LoginBody dto){
JSONObject sessionInfo = wechatUtil.code2Session("appid", "secret", dto.getCode());
String openid = sessionInfo.getString("openid");
String unionid = sessionInfo.getString("unionid");
String sessionKey = sessionInfo.getString("session_key");
if (openid == null) {
return AjaxResult.error("微信授权失败");
}
JSONObject phoneInfo = wechatUtil.decryptPhoneNumber(dto.getEncryptedData(), sessionKey, dto.getIv());
String phone = phoneInfo.getString("phoneNumber");
if (phone == null) {
return AjaxResult.error("获取手机号失败");
}
// 3. 检查手机号是否已被绑定
AppUser existUser = appUserService.getPhone(phone);
if (existUser != null) {
return AjaxResult.error("该手机号已注册");
}
// 4. 创建用户并存储所有信息
AppUser appUser = new AppUser();
appUser.setOpenid(openid);
appUser.setUnionid(unionid);
appUser.setPhone(phone);
appUserService.insertAppUser(appUser);
// 5. 生成系统令牌
AjaxResult ajax = AjaxResult.success();
String token = loginUserIdApp(appUser);
ajax.put(Constants.TOKEN, token);
return ajax;
}
}