微信小程序登录

This commit is contained in:
haown 2025-08-12 15:58:59 +08:00
parent 91dab3510d
commit 2f68392778
35 changed files with 2358 additions and 80 deletions

View File

@ -179,6 +179,12 @@
<version>${wechatpay-apiv3.version}</version>
</dependency>
<!-- redis 缓存操作 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- httpclient 依赖-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>

View File

@ -1,15 +0,0 @@
package com.yf.exam.ability;
/**
* 通用常量
* @author bool
*/
public class Constant {
/**
* 文件上传路径
*/
public static final String FILE_PREFIX = "/upload/file/";
}

View File

@ -1,16 +1,14 @@
package com.yf.exam.ability.shiro.aop;
import com.yf.exam.ability.shiro.jwt.JwtToken;
import com.yf.exam.aspect.utils.InjectUtils;
import com.yf.exam.modules.Constant;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import com.yf.exam.constant.Constants;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
/**
* 鉴权登录拦截器
@ -42,7 +40,7 @@ public class JwtFilter extends BasicHttpAuthenticationFilter {
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader(Constant.TOKEN);
String token = httpServletRequest.getHeader(Constants.TOKEN);
JwtToken jwtToken = new JwtToken(token);
// 提交给realm进行登入如果错误他会抛出异常并被捕获

View File

@ -1,14 +1,15 @@
package com.yf.exam.ability.upload.controller;
import com.yf.exam.ability.Constant;
import com.yf.exam.ability.upload.dto.UploadReqDTO;
import com.yf.exam.ability.upload.dto.UploadRespDTO;
import com.yf.exam.ability.upload.service.UploadService;
import com.yf.exam.constant.Constants;
import com.yf.exam.core.api.ApiRest;
import com.yf.exam.core.api.controller.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
@ -16,9 +17,6 @@ import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 本地文件上传下载请求类
* @author bool
@ -49,7 +47,7 @@ public class UploadController extends BaseController {
* @param request
* @param response
*/
@GetMapping(Constant.FILE_PREFIX+"**")
@GetMapping(Constants.FILE_PREFIX+"**")
@ApiOperation(value = "文件下载", notes = "文件下载")
public void download(HttpServletRequest request, HttpServletResponse response) {
uploadService.download(request, response);

View File

@ -1,6 +1,5 @@
package com.yf.exam.ability.upload.service.impl;
import com.yf.exam.ability.Constant;
import com.yf.exam.ability.upload.config.UploadConfig;
import com.yf.exam.ability.upload.config.UploadPathConfig;
import com.yf.exam.ability.upload.dto.UploadReqDTO;
@ -155,7 +154,7 @@ public class UploadServiceImpl implements UploadService {
*/
public String getRealPath(String uri){
String regx = Constant.FILE_PREFIX+"(.*)";
String regx = Constants.FILE_PREFIX+"(.*)";
// 查找全部变量
Pattern pattern = Pattern.compile(regx);

View File

@ -0,0 +1,48 @@
package com.yf.exam.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @Description 微信小程序参数配置类
* @Author 纪寒
* @Date 2022-09-02 10:52:38
* @Version 1.0
*/
@Data
@Component
@ConfigurationProperties(prefix = "applet-chat-config")
public class AppletChatConfig {
/**
* 小程序id
*/
private String appletId;
/**
* 小程序secret
*/
private String secret;
/**
* 返回国家地区语言版本zh_CN 简体zh_TW 繁体en 英语
*/
private String lang;
/**
* 授权类型
*/
private String grantType;
/**
* 微信小程序事件回调令牌
*/
private String token;
/**
* 微信小程序事件回调消息加密密钥
*/
private String encodingAesKey;
}

View File

@ -50,6 +50,9 @@ public class ShiroConfig {
map.put("/exam/api/sys/user/quick-reg", "anon");
map.put("/exam/api/sys/user/info", "anon");
// 小程序接口
map.put("/examApplet/**", "anon");
// 获取网站基本信息
map.put("/exam/api/sys/config/detail", "anon");

View File

@ -2,6 +2,7 @@ package com.yf.exam.config;
import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import io.swagger.annotations.ApiOperation;
import java.util.Collections;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -16,8 +17,6 @@ import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.Collections;
/**
* Swagger配置
* @author bool
@ -37,7 +36,8 @@ public class SwaggerConfig {
.groupName("考试模块接口")
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.ant("/exam/api/**"))
//.paths(PathSelectors.ant("/exam/api/**"))
.paths(PathSelectors.any())
.build()
.securitySchemes(Collections.singletonList(securityScheme()));
}

View File

@ -0,0 +1,578 @@
package com.yf.exam.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 读取项目相关配置
*
* @author xinyilu
*/
@Component
@ConfigurationProperties(prefix = "xinyilu")
public class XinYiLuConfig {
/**
* 项目名称
*/
private String name;
/**
* 版本
*/
private String version;
/**
* 版权年份
*/
private String copyrightYear;
/**
* 实例演示开关
*/
private boolean demoEnabled;
/**
* 上传路径
*/
private static String profile;
/**
* 获取地址开关
*/
private static boolean addressEnabled;
/**
* 验证码类型
*/
private static String captchaType;
/**
* 柏杏家医云小程序图片存放路径
*/
private String nurserAppletPicture;
/**
* 护理站图片上传存放路径
*/
private String nurserStationPictureUrl;
/**
* 护理站图片上传存放路径
*/
private String nurserStationIntroducePictureUrl;
/**
* 护理站项目图片上传存放路径
*/
private String nurserStationItemPictureUrl;
/**
* 商品图片路径上传存放路径
*/
private String goodsPictureUrl;
/**
* 商品属性图片地址
*/
private String attributePitureUrl;
/**
* 修改会员App用户头像上传
*/
private String headPictureUrl;
/**
* 评价信息图片上传路径
*/
private String evaluatePictureUrl;
/**
* 任务完成上传图片
*/
private String appointmentOrderDetailsUrl;
/**
* 护理机构上传图片
*/
private String nurseStationClassifyUrl;
/**
* 海报图片存放路径
*/
private String posterPictureUrl;
/**
* 海报视频存放路径
*/
private String posterVideoUrl;
/**
* 资讯主缩略图地址
*/
private String leadThumbnailUrl;
/**
* 商品分类图片地址
*/
private String goodsCategoryPicture;
/**
* 护理员App人员头像的上传路径图片上传路径
*/
private String nurseStationAppPersonUrl;
/**
* 获取管理端富文本的上传路径
*/
private String richTextPictureUrl;
/**
* 护理项目分类图标存放地址存放路径
*/
private String nurseItemClassifyUrl;
/**
* 在线客服群二维码存放路径
*/
private String groupQrCodeUrl;
/**
* 科室人员证书图片存放路径
*/
private String certificateUrl;
/**
* 健康咨询 科室人员头像地址
*/
private String personPictureUrl;
/**
* 护理人员证书地址
*/
private String personCertificateUrl;
/**
* 审核护理人员证书地址
*/
private String personCertificateCheckUrl;
/**
* 医路
*/
private String yiLuYouPinStoreName;
/**
* 护理员App上传路径
*/
private String appFileName;
/**
* 泉医到家App文件存放路径
*/
private String appFilePath;
/**
* 培训分类图片路径
*/
private String trainingCategoryPictureUrl;
/**
* 培训项目logo图片路径
*/
private String trainingItemCoverUrl;
/**
* 培训项目海报图片路径
*/
private String trainingItemPosterUrl;
/**
* 培训项目海报图片路径
*/
private String trainingItemContentUrl;
/**
* 培训项目章节视频存放地址
*/
private String itemDirectoryUrl;
/**
* 微信好有邀请二维码存放路径
*/
private String personalWeChatCodeUrl;
/**
* 护理站二维码存放路径
*/
private String stationWechatCodeUrl;
/**
* 实人认证身份证照片地址
*/
private String cardNoPicUrl;
/**
* 预约订单护理项目告知书签字图片地址
*/
private String itemInformUrl;
public String getItemInformUrl() {
return itemInformUrl;
}
public void setItemInformUrl(String itemInformUrl) {
this.itemInformUrl = itemInformUrl;
}
public String getCardNoPicUrl() {
return cardNoPicUrl;
}
public void setCardNoPicUrl(String cardNoPicUrl) {
this.cardNoPicUrl = cardNoPicUrl;
}
public String getStationWechatCodeUrl() {
return stationWechatCodeUrl;
}
public void setStationWechatCodeUrl(String stationWechatCodeUrl) {
this.stationWechatCodeUrl = stationWechatCodeUrl;
}
public String getPersonalWeChatCodeUrl() {
return personalWeChatCodeUrl;
}
public void setPersonalWeChatCodeUrl(String personalWeChatCodeUrl) {
this.personalWeChatCodeUrl = personalWeChatCodeUrl;
}
public String getAppFilePath() {
return appFilePath;
}
public void setAppFilePath(String appFilePath) {
this.appFilePath = appFilePath;
}
public String getYiLuYouPinStoreName() {
return yiLuYouPinStoreName;
}
public void setYiLuYouPinStoreName(String yiLuYouPinStoreName) {
this.yiLuYouPinStoreName = yiLuYouPinStoreName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getCopyrightYear() {
return copyrightYear;
}
public void setCopyrightYear(String copyrightYear) {
this.copyrightYear = copyrightYear;
}
public boolean isDemoEnabled() {
return demoEnabled;
}
public void setDemoEnabled(boolean demoEnabled) {
this.demoEnabled = demoEnabled;
}
public static String getProfile() {
return profile;
}
public void setProfile(String profile) {
XinYiLuConfig.profile = profile;
}
public static boolean isAddressEnabled() {
return addressEnabled;
}
public void setAddressEnabled(boolean addressEnabled) {
XinYiLuConfig.addressEnabled = addressEnabled;
}
public static String getCaptchaType() {
return captchaType;
}
public void setCaptchaType(String captchaType) {
XinYiLuConfig.captchaType = captchaType;
}
/**
* 获取导入上传路径
*/
public static String getImportPath() {
return getProfile() + "/import";
}
/**
* 获取头像上传路径
*/
public static String getAvatarPath() {
return getProfile() + "/avatar";
}
/**
* 获取下载路径
*/
public static String getDownloadPath() {
return getProfile() + "/download/";
}
/**
* 获取上传路径
*/
public static String getUploadPath() {
return getProfile() + "/upload";
}
public String getNurserAppletPicture() {
return nurserAppletPicture;
}
public void setNurserAppletPicture(String nurserAppletPicture) {
this.nurserAppletPicture = nurserAppletPicture;
}
public String getNurserStationPictureUrl() {
return nurserStationPictureUrl;
}
public void setNurserStationPictureUrl(String nurserStationPictureUrl) {
this.nurserStationPictureUrl = nurserStationPictureUrl;
}
public String getNurserStationIntroducePictureUrl() {
return nurserStationIntroducePictureUrl;
}
public void setNurserStationIntroducePictureUrl(String nurserStationIntroducePictureUrl) {
this.nurserStationIntroducePictureUrl = nurserStationIntroducePictureUrl;
}
public String getNurserStationItemPictureUrl() {
return nurserStationItemPictureUrl;
}
public void setNurserStationItemPictureUrl(String nurserStationItemPictureUrl) {
this.nurserStationItemPictureUrl = nurserStationItemPictureUrl;
}
public String getGoodsPictureUrl() {
return goodsPictureUrl;
}
public void setGoodsPictureUrl(String goodsPictureUrl) {
this.goodsPictureUrl = goodsPictureUrl;
}
public String getHeadPictureUrl() {
return headPictureUrl;
}
public void setHeadPictureUrl(String headPictureUrl) {
this.headPictureUrl = headPictureUrl;
}
public String getAppointmentOrderDetailsUrl() {
return appointmentOrderDetailsUrl;
}
public void setAppointmentOrderDetailsUrl(String appointmentOrderDetailsUrl) {
this.appointmentOrderDetailsUrl = appointmentOrderDetailsUrl;
}
public String getAttributePitureUrl() {
return attributePitureUrl;
}
public void setAttributePitureUrl(String attributePitureUrl) {
this.attributePitureUrl = attributePitureUrl;
}
public String getAppFileName() {
return appFileName;
}
public void setAppFileName(String appFileName) {
this.appFileName = appFileName;
}
public String getNurseStationAppPersonUrl() {
return nurseStationAppPersonUrl;
}
public void setNurseStationAppPersonUrl(String nurseStationAppPersonUrl) {
this.nurseStationAppPersonUrl = nurseStationAppPersonUrl;
}
public String getEvaluatePictureUrl() {
return evaluatePictureUrl;
}
public void setEvaluatePictureUrl(String evaluatePictureUrl) {
this.evaluatePictureUrl = evaluatePictureUrl;
}
public String getRichTextPictureUrl() {
return richTextPictureUrl;
}
public void setRichTextPictureUrl(String richTextPictureUrl) {
this.richTextPictureUrl = richTextPictureUrl;
}
public String getGoodsCategoryPicture() {
return goodsCategoryPicture;
}
public void setGoodsCategoryPicture(String goodsCategoryPicture) {
this.goodsCategoryPicture = goodsCategoryPicture;
}
public String getNurseStationClassifyUrl() {
return nurseStationClassifyUrl;
}
public void setNurseStationClassifyUrl(String nurseStationClassifyUrl) {
this.nurseStationClassifyUrl = nurseStationClassifyUrl;
}
public String getNurseItemClassifyUrl() {
return nurseItemClassifyUrl;
}
public void setNurseItemClassifyUrl(String nurseItemClassifyUrl) {
this.nurseItemClassifyUrl = nurseItemClassifyUrl;
}
public String getPosterPictureUrl() {
return posterPictureUrl;
}
public void setPosterPictureUrl(String posterPictureUrl) {
this.posterPictureUrl = posterPictureUrl;
}
public String getLeadThumbnailUrl() {
return leadThumbnailUrl;
}
public void setLeadThumbnailUrl(String leadThumbnailUrl) {
this.leadThumbnailUrl = leadThumbnailUrl;
}
public String getPosterVideoUrl() {
return posterVideoUrl;
}
public void setPosterVideoUrl(String posterVideoUrl) {
this.posterVideoUrl = posterVideoUrl;
}
public String getGroupQrCodeUrl() {
return groupQrCodeUrl;
}
public void setGroupQrCodeUrl(String groupQrCodeUrl) {
this.groupQrCodeUrl = groupQrCodeUrl;
}
public String getCertificateUrl() {
return certificateUrl;
}
public void setCertificateUrl(String certificateUrl) {
this.certificateUrl = certificateUrl;
}
public String getPersonPictureUrl() {
return personPictureUrl;
}
public void setPersonPictureUrl(String personPictureUrl) {
this.personPictureUrl = personPictureUrl;
}
public String getPersonCertificateUrl() {
return personCertificateUrl;
}
public void setPersonCertificateUrl(String personCertificateUrl) {
this.personCertificateUrl = personCertificateUrl;
}
public String getPersonCertificateCheckUrl() {
return personCertificateCheckUrl;
}
public void setPersonCertificateCheckUrl(String personCertificateCheckUrl) {
this.personCertificateCheckUrl = personCertificateCheckUrl;
}
public String getTrainingCategoryPictureUrl() {
return trainingCategoryPictureUrl;
}
public void setTrainingCategoryPictureUrl(String trainingCategoryPictureUrl) {
this.trainingCategoryPictureUrl = trainingCategoryPictureUrl;
}
public String getTrainingItemCoverUrl() {
return trainingItemCoverUrl;
}
public void setTrainingItemCoverUrl(String trainingItemCoverUrl) {
this.trainingItemCoverUrl = trainingItemCoverUrl;
}
public String getTrainingItemPosterUrl() {
return trainingItemPosterUrl;
}
public void setTrainingItemPosterUrl(String trainingItemPosterUrl) {
this.trainingItemPosterUrl = trainingItemPosterUrl;
}
public String getTrainingItemContentUrl() {
return trainingItemContentUrl;
}
public void setTrainingItemContentUrl(String trainingItemContentUrl) {
this.trainingItemContentUrl = trainingItemContentUrl;
}
public String getItemDirectoryUrl() {
return itemDirectoryUrl;
}
public void setItemDirectoryUrl(String itemDirectoryUrl) {
this.itemDirectoryUrl = itemDirectoryUrl;
}
}

View File

@ -5,7 +5,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @Description 新医路商户号微信支付参数配置类
* @Description 山东省公共卫生学会商户号微信支付参数配置类
* @Author 纪寒
* @Date 2022-10-17 16:55:04
* @Version 1.0
@ -16,27 +16,27 @@ import org.springframework.stereotype.Component;
public class XylWeChatPaymentConfig {
/**
* 新医路商户号
* 山东省公共卫生学会商户号
*/
private String xylMchId;
/**
* 新医路API证书序号getXylVerifier
* 山东省公共卫生学会证书序号getXylVerifier
*/
private String xylMchSerialNo;
/**
* 新医路私钥文件
* 山东省公共卫生学会私钥文件
*/
private String xylPrivateKeyPath;
/**
* 新医路API V3版本密钥
* 山东省公共卫生学会API V3版本密钥
*/
private String xylPaymentKey;
/**
* 新医路支付回调地址
* 山东省公共卫生学会支付回调地址
*/
private String xylWeChatNotifyUrl;
}

View File

@ -19,7 +19,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
/**
* @Description 新医路商户号微信支付核心配置类
* @Description 山东省公共卫生学会商户号微信支付核心配置类
* @Author 纪寒
* @Date 2022-10-17 18:56:22
* @Version 1.0
@ -32,7 +32,7 @@ public class XylWeChatPaymentUtilConfig {
private XylWeChatPaymentConfig xylWeChatPaymentConfig;
/**
* 获取新医路签名信息
* 获取山东省公共卫生学会签名信息
*
* @return 签名信息
* @throws Exception 异常信息
@ -54,36 +54,36 @@ public class XylWeChatPaymentUtilConfig {
}
/**
* 获取新医路商户号带有签名的http请求对象
* 获取山东省公共卫生学会商户号带有签名的http请求对象
*
* @return CloseableHttpClient对象
*/
@Bean(name = "xinYiLuWeChatPayClient")
public CloseableHttpClient getXinYiLuWeChatPayClient(Verifier xylVerifier) {
log.info("开始获取新医路商户带有签名信息的http请求对象........");
log.info("开始获取山东省公共卫生学会商户带有签名信息的http请求对象........");
//获取商户私钥
PrivateKey privateKey = getPrivateKey(xylWeChatPaymentConfig.getXylPrivateKeyPath());
CloseableHttpClient closeableHttpClient = null;
try {
log.info("新医路签名验证证书xinYiLuCertificatesVerifier{}", xylVerifier);
log.info("山东省公共卫生学会签名验证证书xinYiLuCertificatesVerifier{}", xylVerifier);
//通过WeChatPayHttpClientBuilder构造的HttpClient会自动的处理签名和验签并进行证书自动更新
closeableHttpClient = WechatPayHttpClientBuilder.create()
.withMerchant(xylWeChatPaymentConfig.getXylMchId(), xylWeChatPaymentConfig.getXylMchSerialNo(), privateKey)
.withValidator(new WechatPay2Validator(xylVerifier)).build();
} catch (Exception e) {
log.error("新医路验证签名信息失败,失败信息:{}", e.getMessage());
log.error("山东省公共卫生学会验证签名信息失败,失败信息:{}", e.getMessage());
}
return closeableHttpClient;
}
/**
* 获取新医路商户号无签名的http请求对象
* 获取山东省公共卫生学会商户号无签名的http请求对象
*
* @return CloseableHttpClient对象
*/
@Bean(name = "xinYiLuWeChatPayNoSignClient")
public CloseableHttpClient getXinYiLuWeChatPayNoSignClient() {
log.info("获取新医路商户的无签名信息的http请求对象.........");
log.info("获取山东省公共卫生学会商户的无签名信息的http请求对象.........");
//获取商户私钥
PrivateKey privateKey = getPrivateKey(xylWeChatPaymentConfig.getXylPrivateKeyPath());
//通过WeChatPayHttpClientBuilder构造的HttpClient会自动的处理签名和验签并进行证书自动更新
@ -102,8 +102,8 @@ public class XylWeChatPaymentUtilConfig {
try {
return PemUtil.loadPrivateKey(new ClassPathResource(filename).getInputStream());
} catch (Exception e) {
log.error("新医路商户私钥文件不存在,错误原因:{}", e.getMessage());
throw new ServiceException("新医路商户私钥文件不存在,请联系管理员!");
log.error("山东省公共卫生学会商户私钥文件不存在,错误原因:{}", e.getMessage());
throw new ServiceException("山东省公共卫生学会商户私钥文件不存在,请联系管理员!");
}
}
}

View File

@ -7,6 +7,21 @@ package com.yf.exam.constant;
*/
public class Constants {
/**
* UTF-8 字符集
*/
public static final String UTF8 = "UTF-8";
/**
* 会话
*/
public static final String TOKEN = "token";
/**
* 文件上传路径
*/
public static final String FILE_PREFIX = "/upload/file/";
/**
* 身份证正面照片路径上传
*/
@ -42,6 +57,11 @@ public class Constants {
*/
public static final String sign_picture_type = "signPictureUrl";
/**
* 微信小程序ACCESS_TOKEN前缀
*/
public static final String EXAM_APPLET_ACCESS_TOKEN = "EXAM_APPLET_ACCESS_TOKEN:";
}

View File

@ -0,0 +1,165 @@
package com.yf.exam.core.domain;
import java.util.HashMap;
/**
* 操作消息提醒
*
* @author xinyilu
*/
public class AjaxResult extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
/**
* 状态码
*/
public static final String CODE_TAG = "code";
/**
* 返回内容
*/
public static final String MSG_TAG = "msg";
/**
* 数据对象
*/
public static final String DATA_TAG = "data";
/**
* 成功默认消息
*/
private static final Integer CODE_SUCCESS = 0;
private static final String MSG_SUCCESS = "操作成功!";
/**
* 失败默认消息
*/
private static final Integer CODE_FAILURE = 1;
private static final String MSG_FAILURE = "请求失败!";
/**
* 初始化一个新创建的 AjaxResult 对象使其表示一个空消息
*/
public AjaxResult() {
}
/**
* 初始化一个新创建的 AjaxResult 对象
*
* @param code 状态码
* @param msg 返回内容
*/
public AjaxResult(int code, String msg) {
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
}
/**
* 初始化一个新创建的 AjaxResult 对象
*
* @param code 状态码
* @param msg 返回内容
* @param data 数据对象
*/
public AjaxResult(int code, String msg, Object data) {
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
if (!(data == null)) {
super.put(DATA_TAG, data);
}
}
/**
* 返回成功消息
*
* @return 成功消息
*/
public static AjaxResult success() {
return AjaxResult.success("操作成功");
}
/**
* 返回成功数据
*
* @return 成功消息
*/
public static AjaxResult success(Object data) {
return AjaxResult.success("操作成功", data);
}
/**
* 返回成功消息
*
* @param msg 返回内容
* @return 成功消息
*/
public static AjaxResult success(String msg) {
return AjaxResult.success(msg, null);
}
/**
* 返回成功消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 成功消息
*/
public static AjaxResult success(String msg, Object data) {
return new AjaxResult(CODE_SUCCESS, msg, data);
}
/**
* 返回错误消息
*
* @return
*/
public static AjaxResult error() {
return AjaxResult.error("操作失败");
}
/**
* 返回错误消息
*
* @param msg 返回内容
* @return 警告消息
*/
public static AjaxResult error(String msg) {
return AjaxResult.error(msg, null);
}
/**
* 返回错误消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 警告消息
*/
public static AjaxResult error(String msg, Object data) {
return new AjaxResult(CODE_FAILURE, msg, data);
}
/**
* 返回错误消息
*
* @param code 状态码
* @param msg 返回内容
* @return 警告消息
*/
public static AjaxResult error(int code, String msg) {
return new AjaxResult(code, msg, null);
}
/**
* 方便链式调用
*
* @param key
* @param value
* @return 数据对象
*/
@Override
public AjaxResult put(String key, Object value) {
super.put(key, value);
return this;
}
}

View File

@ -0,0 +1,226 @@
package com.yf.exam.core.redis;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
/**
* spring redis 工具类
*
* @author xinyilu
**/
@SuppressWarnings(value = {"unchecked", "rawtypes"})
@Component
public class RedisCache {
@Resource
public RedisTemplate redisTemplate;
/**
* 缓存基本的对象IntegerString实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
*/
public <T> void setCacheObject(final String key, final T value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* 缓存基本的对象IntegerString实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
* @param timeout 时间
* @param timeUnit 时间颗粒度
*/
public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @return true=设置成功false=设置失败
*/
public boolean expire(final String key, final long timeout) {
return expire(key, timeout, TimeUnit.SECONDS);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @param unit 时间单位
* @return true=设置成功false=设置失败
*/
public boolean expire(final String key, final long timeout, final TimeUnit unit) {
return redisTemplate.expire(key, timeout, unit);
}
/**
* 获得缓存的基本对象
*
* @param key 缓存键值
* @return 缓存键值对应的数据
*/
public <T> T getCacheObject(final String key) {
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
}
/**
* 删除单个对象
*
* @param key
*/
public boolean deleteObject(final String key) {
return redisTemplate.delete(key);
}
/**
* 删除集合对象
*
* @param collection 多个对象
* @return
*/
public long deleteObject(final Collection collection) {
return redisTemplate.delete(collection);
}
/**
* 缓存List数据
*
* @param key 缓存的键值
* @param dataList 待缓存的List数据
* @return 缓存的对象
*/
public <T> long setCacheList(final String key, final List<T> dataList) {
Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
return count == null ? 0 : count;
}
/**
* 获得缓存的list对象
*
* @param key 缓存的键值
* @return 缓存键值对应的数据
*/
public <T> List<T> getCacheList(final String key) {
return redisTemplate.opsForList().range(key, 0, -1);
}
/**
* 缓存Set
*
* @param key 缓存键值
* @param dataSet 缓存的数据
* @return 缓存数据的对象
*/
public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {
BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
Iterator<T> it = dataSet.iterator();
while (it.hasNext()) {
setOperation.add(it.next());
}
return setOperation;
}
/**
* 获得缓存的set
*
* @param key
* @return
*/
public <T> Set<T> getCacheSet(final String key) {
return redisTemplate.opsForSet().members(key);
}
/**
* 缓存Map
*
* @param key
* @param dataMap
*/
public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
if (dataMap != null) {
redisTemplate.opsForHash().putAll(key, dataMap);
}
}
/**
* 获得缓存的Map
*
* @param key
* @return
*/
public <T> Map<String, T> getCacheMap(final String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* 往Hash中存入数据
*
* @param key Redis键
* @param hKey Hash键
* @param value
*/
public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
redisTemplate.opsForHash().put(key, hKey, value);
}
/**
* 获取Hash中的数据
*
* @param key Redis键
* @param hKey Hash键
* @return Hash中的对象
*/
public <T> T getCacheMapValue(final String key, final String hKey) {
HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
return opsForHash.get(key, hKey);
}
/**
* 删除Hash中的数据
*
* @param key
* @param hKey
*/
public void delCacheMapValue(final String key, final String hKey) {
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.delete(key, hKey);
}
/**
* 获取多个Hash中的数据
*
* @param key Redis键
* @param hKeys Hash键集合
* @return Hash对象集合
*/
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
return redisTemplate.opsForHash().multiGet(key, hKeys);
}
/**
* 获得缓存的基本对象列表
*
* @param pattern 字符串前缀
* @return 对象列表
*/
public Collection<String> keys(final String pattern) {
return redisTemplate.keys(pattern);
}
}

View File

@ -0,0 +1,277 @@
package com.yf.exam.core.utils;
import com.alibaba.fastjson2.JSON;
import com.yf.exam.config.AppletChatConfig;
import com.yf.exam.config.XinYiLuConfig;
import com.yf.exam.constant.Constants;
import com.yf.exam.core.exception.ServiceException;
import com.yf.exam.core.utils.http.HttpUtils;
import com.yf.exam.modules.applet.entity.AppletAccessToken;
import com.yf.exam.modules.applet.vo.AppletLoginVO;
import com.yf.exam.modules.applet.vo.AppletPhoneVO;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
/**
* @Description 微信小程序工具类
* @Author 纪寒
* @Date 2022-08-17 16:11:10
* @Version 1.0
*/
@Slf4j
@Component
public class AppletChatUtil {
private static RedisTemplate<String, Object> redisTemplate;
private static AppletChatConfig appletChatConfig;
@Resource
public void redisTemplate(RedisTemplate<String, Object> redisTemplate) {
AppletChatUtil.redisTemplate = redisTemplate;
}
@Resource
public void appletChatConfig(AppletChatConfig appletChatConfig) {
AppletChatUtil.appletChatConfig = appletChatConfig;
}
/**
* 返回成功状态码
*/
private static final int SUCCESS_CODE = 0;
/**
* 小程序登录url
*/
private static final String APPLET_LOGIN_URL = "https://api.weixin.qq.com/sns/jscode2session";
/**
* 小程序accessToken的url
*/
private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential";
/**
* 小程序获取用户手机号的url
*/
private static final String PHONE_NUMBER_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=";
/**
* 获取小程序二维码接口地址
*/
private static final String GET_APPLET_CODE_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=";
/**
* JSON数据格式标识
*/
private static final String JSON_DATA_FORMAT = "Content-Type: application/json; encoding=utf-8";
/**
* 生成小程序二维码图片失败标识
*/
private static final String FAIL = "FAIL";
/**
* 根据登录编码获取微信用户信息
*
* @param appletId 小程序id
* @param secret 小程序秘钥
* @param code 登录凭证码
* @param grantType 授权类型
* @return 登录信息
*/
public static AppletLoginVO getAppletLoginInfo(String appletId, String secret, String code, String grantType) {
//请求地址
String appletLoginUrl = APPLET_LOGIN_URL
+ "?appid=" + appletId
+ "&secret=" + secret
+ "&js_code=" + code
+ "&grant_type=" + grantType;
//发送请求
String result = HttpUtils.sendGet(appletLoginUrl);
if (StringUtils.isBlank(result)) {
throw new ServiceException("获取微信小程序用户信息失败");
}
return JSON.parseObject(result, AppletLoginVO.class);
}
/**
* 获取微信小程序的accessToken
*
* @param appletId 小程序id
* @param secret 小程序秘钥
* @return accessToken信息
*/
public static AppletAccessToken getAppletAccessToken(String appletId, String secret) {
//请求路径
String accessTokenUrl = ACCESS_TOKEN_URL + "&appid=" + appletId + "&secret=" + secret;
//发送请求
String result = HttpUtils.sendGet(accessTokenUrl);
if (StringUtils.isBlank(result)) {
throw new ServiceException("获取微信小程序accessToken信息失败");
}
return JSON.parseObject(result, AppletAccessToken.class);
}
/**
* 获取微信小程序的手机号码
*
* @param code 登录凭证
* @param accessToken 小程序accessToken
* @return 手机信息
*/
public static AppletPhoneVO getAppletPhoneInfo(String code, String accessToken) {
//请求地址
String phoneUrl = PHONE_NUMBER_URL + accessToken;
//请求参数
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("code", code);
String param = JSON.toJSONString(paramMap);
//发送POST请求
String result = HttpUtils.sendPostJson(phoneUrl, param);
if (StringUtils.isBlank(result)) {
throw new ServiceException("获取微信小程序手机号失败");
}
return JSON.parseObject(result, AppletPhoneVO.class);
}
/**
* 递归获取小程序二维码信息
*
* @param weChatCodeId 输入id
* @param failFlag 调用失败标识
* @param fileName 文件名称
* @param appletPageUrl 二维码跳转路径
* @param filePathWeChatCodeUrl 文件保存路径
* @return java.lang.String 二维码地址
*/
public static String createAppletCode(Long weChatCodeId, boolean failFlag, String fileName, String appletPageUrl, String filePathWeChatCodeUrl) {
if (BooleanUtils.isTrue(failFlag)) {
//删除原有Redis中的key值
String accessTokenKey = Constants.EXAM_APPLET_ACCESS_TOKEN + "accessToken";
Object object = redisTemplate.opsForValue().get(accessTokenKey);
if (Objects.nonNull(object)) {
redisTemplate.delete(accessTokenKey);
}
}
//删除原有Redis中缓存的AccessToken
String appletAccessToken = getAppletAccessToken();
if (StringUtils.isBlank(appletAccessToken)) {
throw new ServiceException("获取小程序凭证信息失败,请联系管理员!");
}
Map<String, Object> params = new HashMap<>();
params.put("scene", weChatCodeId);
params.put("page", appletPageUrl);
String body = JSON.toJSONString(params);
String filePath = XinYiLuConfig.getProfile() + filePathWeChatCodeUrl + "/" + weChatCodeId;
//调用微信接口获取小程序二维码并将其上传到服务器中
return getAppletCodePicture(appletAccessToken, body, filePath, fileName);
}
/**
* 根据小程序AccessToken获取小程序的二维码图片
*
* @param accessToken 微信小程序的accessToken
* @param body 接口传输参数
* @param filePath 文件路径
* @param fileName 文件名称
* @return 小程序的二维码图片
*/
public static String getAppletCodePicture(String accessToken, String body, String filePath, String fileName) {
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost(GET_APPLET_CODE_URL + accessToken);
try {
StringEntity entity = new StringEntity(body, "UTF-8");
entity.setContentType("image/png");
httpPost.setEntity(entity);
HttpResponse response = httpClient.execute(httpPost);
String contentType = response.getEntity().getContentType().toString();
if (StringUtils.equals(contentType, JSON_DATA_FORMAT)) {
return FAIL;
}
String absolutePath = getAbsoluteFile(filePath, fileName).getAbsolutePath();
try (InputStream inputStream = response.getEntity().getContent(); FileOutputStream out = new FileOutputStream(absolutePath)) {
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
out.flush();
}
return absolutePath;
} catch (Exception e) {
log.error("获取小程序二维码接口调用失败,失败原因为:{}", e.getMessage());
return null;
}
}
/**
* 创建文件路径
*
* @param uploadDir 路径名称
* @param fileName 文件名称
* @return File 文件目录
*/
public static File getAbsoluteFile(String uploadDir, String fileName) {
File file = new File(uploadDir + File.separator + fileName);
if (!file.exists()) {
if (!file.getParentFile().exists()) {
boolean mkdirs = file.getParentFile().mkdirs();
if (BooleanUtils.isFalse(mkdirs)) {
throw new ServiceException("文件路径创建失败,请联系管理员!");
}
}
}
return file;
}
/**
* 获取小程序AccessToken方法
*
* @return 小程序的AccessToken
*/
public static String getAppletAccessToken() {
String accessToken;
String accessTokenKey = Constants.EXAM_APPLET_ACCESS_TOKEN + "accessToken";
//从Redis中取出accessToken
Object object = redisTemplate.opsForValue().get(accessTokenKey);
if (Objects.isNull(object)) {
//没有获取accessToken
AppletAccessToken appletAccessToken = AppletChatUtil.getAppletAccessToken(appletChatConfig.getAppletId(), appletChatConfig.getSecret());
if (Objects.isNull(appletAccessToken)) {
throw new ServiceException("获取微信小程序accessToken信息失败");
}
if (Objects.nonNull(appletAccessToken.getErrcode()) && appletAccessToken.getErrcode() != SUCCESS_CODE) {
throw new ServiceException("获取微信小程序accessToken信息失败失败信息为" + appletAccessToken.getErrmsg());
}
if (StringUtils.isBlank(appletAccessToken.getAccessToken())) {
throw new ServiceException("accessToken信息为空");
}
//存入Redis中
redisTemplate.opsForValue().set(accessTokenKey, appletAccessToken.getAccessToken(), 3600, TimeUnit.SECONDS);
accessToken = appletAccessToken.getAccessToken();
} else {
accessToken = (String) object;
}
return accessToken;
}
}

View File

@ -0,0 +1,43 @@
package com.yf.exam.core.utils.http;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import javax.servlet.ServletRequest;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 通用http工具封装
*
* @author xinyilu
*/
public class HttpHelper {
private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class);
public static String getBodyString(ServletRequest request) {
StringBuilder sb = new StringBuilder();
BufferedReader reader = null;
try (InputStream inputStream = request.getInputStream()) {
reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
String line = "";
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
LOGGER.warn("getBodyString出现问题");
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
LOGGER.error(ExceptionUtils.getMessage(e));
}
}
}
return sb.toString();
}
}

View File

@ -0,0 +1,297 @@
package com.yf.exam.core.utils.http;
import com.yf.exam.constant.Constants;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 通用http发送方法
*
* @author xinyilu
*/
public class HttpUtils {
private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
/**
* 向指定 URL 发送GET方法的请求
*
* @param url 发送请求的 URL
* @return 所代表远程资源的响应结果
*/
public static String sendGet(String url) {
return sendGet(url, StringUtils.EMPTY);
}
/**
* 向指定 URL 发送GET方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数请求参数应该是 name1=value1&name2=value2 的形式
* @return 所代表远程资源的响应结果
*/
public static String sendGet(String url, String param) {
return sendGet(url, param, Constants.UTF8);
}
/**
* 向指定 URL 发送GET方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数请求参数应该是 name1=value1&name2=value2 的形式
* @param contentType 编码类型
* @return 所代表远程资源的响应结果
*/
public static String sendGet(String url, String param, String contentType) {
StringBuilder result = new StringBuilder();
BufferedReader in = null;
try {
String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
log.info("sendGet - {}", urlNameString);
URL realUrl = new URL(urlNameString);
URLConnection connection = realUrl.openConnection();
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.connect();
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
String line;
while ((line = in.readLine()) != null) {
result.append(line);
}
log.info("recv - {}", result);
} catch (ConnectException e) {
log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
} catch (SocketTimeoutException e) {
log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
} catch (IOException e) {
log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
} catch (Exception e) {
log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
} finally {
try {
if (in != null) {
in.close();
}
} catch (Exception ex) {
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
}
}
return result.toString();
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数请求参数应该是 name1=value1&name2=value2 的形式
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param) {
PrintWriter out = null;
BufferedReader in = null;
StringBuilder result = new StringBuilder();
try {
log.info("sendPost - {}", url);
URL realUrl = new URL(url);
URLConnection conn = realUrl.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setDoOutput(true);
conn.setDoInput(true);
out = new PrintWriter(conn.getOutputStream());
out.print(param);
out.flush();
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
String line;
while ((line = in.readLine()) != null) {
result.append(line);
}
log.info("recv - {}", result);
} catch (ConnectException e) {
log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
} catch (SocketTimeoutException e) {
log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
} catch (IOException e) {
log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
} catch (Exception e) {
log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
}
}
return result.toString();
}
public static String sendSSLPost(String url, String param) {
StringBuilder result = new StringBuilder();
String urlNameString = url + "?" + param;
try {
log.info("sendSSLPost - {}", urlNameString);
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom());
URL console = new URL(urlNameString);
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setSSLSocketFactory(sc.getSocketFactory());
conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
conn.connect();
InputStream is = conn.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String ret = "";
while ((ret = br.readLine()) != null) {
if (ret != null && !"".equals(ret.trim())) {
result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
}
}
log.info("recv - {}", result);
conn.disconnect();
br.close();
} catch (ConnectException e) {
log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
} catch (SocketTimeoutException e) {
log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
} catch (IOException e) {
log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
} catch (Exception e) {
log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
}
return result.toString();
}
private static class TrustAnyTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}
private static class TrustAnyHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
/**
* 将通知参数转化为字符串
*
* @param request 请求信息
* @return 请求参数信息
*/
public static String readRequestData(HttpServletRequest request) {
BufferedReader br = null;
try {
StringBuilder result = new StringBuilder();
br = request.getReader();
for (String line; (line = br.readLine()) != null; ) {
if (result.length() > 0) {
result.append("\n");
}
result.append(line);
}
return result.toString();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 发送json格式的POST类型的http请求
*
* @param url 请求地址
* @param param json格式的请求参数
* @return String 返回值信息
*/
public static String sendPostJson(String url, String param) {
String result = null;
CloseableHttpClient httpClient = null;
CloseableHttpResponse response = null;
try {
httpClient = HttpClients.createDefault();
// 字符串编码
StringEntity entity = new StringEntity(param, Consts.UTF_8);
// 设置content-type
entity.setContentType("application/json");
HttpPost httpPost = new HttpPost(url);
httpPost.setConfig(RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(10000).build());
// 防止被当成攻击添加的
httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/54.0.2840.87 Safari/537.36");
// 接收参数设置
httpPost.setHeader("Accept", "application/json");
httpPost.setEntity(entity);
response = httpClient.execute(httpPost);
HttpEntity httpEntity = response.getEntity();
result = EntityUtils.toString(httpEntity);
} catch (Exception e) {
log.warn("post请求发送失败请求路径[{}],请求参数:[{}],异常信息:[{}]", url, param, e);
} finally {
HttpClientUtils.closeQuietly(response);
HttpClientUtils.closeQuietly(httpClient);
}
return result;
}
}

View File

@ -0,0 +1,62 @@
package com.yf.exam.core.utils.regex;
import com.yf.exam.core.exception.ServiceException;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
/**
* @Description 正则表达式工具类
* @Author 纪寒
* @Date 2022-08-24 13:58:48
* @Version 1.0
*/
@Component
public class RegexUtil {
/**
* 校验手机号码
*
* @param phone 手机号码
* @return 校验结果
*/
public boolean regexPhone(String phone) {
if (StringUtils.isBlank(phone)) {
throw new ServiceException("手机号码不能为空!");
}
//校验手机号
String regex = "^(((13[0-9]{1})|(14[0-9]{1})|(15[0-9]{1})|(16[2567]{1})|(17[0-9]{1})|(18[0-9]{1})|(19[0-9]{1}))+\\d{8})$";
return Pattern.matches(regex, phone);
}
/**
* 校验身份证号码
*
* @param cardNo 身份证号码
* @return 校验结果
*/
public boolean regexCardNo(String cardNo) {
if (StringUtils.isBlank(cardNo)) {
throw new ServiceException("身份证号不能为空!");
}
//校验身份证号码
String regex = "(^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$)|" +
"(^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}$)";
return Pattern.matches(regex, cardNo);
}
/**
* 校验座机手机号码
*
* @param seatNumber 座机手机号码
* @return 校验结果
*/
public boolean regexSeatNumber(String seatNumber) {
if (StringUtils.isBlank(seatNumber)) {
throw new ServiceException("手机号码不能为空!");
}
//校验座机手机号
String regex = "^(([0-9]{4,13}))";
return Pattern.matches(regex, seatNumber);
}
}

View File

@ -1,14 +0,0 @@
package com.yf.exam.modules;
/**
* 通用常量
* @author bool
*/
public class Constant {
/**
* 会话
*/
public static final String TOKEN = "token";
}

View File

@ -0,0 +1,92 @@
package com.yf.exam.modules.applet.controller;
import com.yf.exam.core.api.controller.BaseController;
import com.yf.exam.core.domain.AjaxResult;
import com.yf.exam.core.utils.regex.RegexUtil;
import com.yf.exam.modules.applet.service.AppletLoginService;
import com.yf.exam.modules.sys.user.dto.SysUserDTO;
import com.yf.exam.modules.sys.user.service.SysUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javax.annotation.Resource;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @description: 微信小程序登录注册控制器
* @author: haown
* @create: 2025-08-11 16:05
**/
@Api(tags={"微信小程序"})
@RestController
@RequestMapping("/examApplet")
public class AppletLoginController extends BaseController {
@Resource
private AppletLoginService appletLoginService;
@Resource
private SysUserService sysUserService;
@Resource
private RegexUtil regexUtil;
/**
* 注册用户信息
*
* @param reqDTO 输入参数
* @return 结果
*/
@ApiOperation(value = "注册用户信息")
@PostMapping("/register")
public AjaxResult registerUserInfo(@RequestBody SysUserDTO reqDTO) {
//校验手机号
boolean regexPhone = regexUtil.regexPhone(StringUtils.isBlank(reqDTO.getPhone()) ? "" : reqDTO.getPhone());
if (BooleanUtils.isFalse(regexPhone)) {
return AjaxResult.error("您输入的手机号不正确,请重新输入!");
}
//校验身份证号
boolean cardNo = regexUtil.regexCardNo(StringUtils.isBlank(reqDTO.getUserName()) ? "" : reqDTO.getUserName());
if (BooleanUtils.isFalse(cardNo)) {
return AjaxResult.error("您输入的身份证号不正确,请重新输入!");
}
return AjaxResult.success(sysUserService.reg(reqDTO));
}
/**
* 根据登录凭证获取用户的登录信息
*
* @param loginCode 登录凭证
* @return 微信用户登录信息
*/
@ApiOperation(value = "根据登录凭证获取用户的登录信息")
@GetMapping("/appletLogin")
public AjaxResult appletLogin(@RequestParam("loginCode") String loginCode) {
if (StringUtils.isBlank(loginCode)) {
return AjaxResult.error("登录凭证编码不能为空!");
}
//if (StringUtils.isBlank(phoneCode)) {
// return AjaxResult.error("获取手机号凭证不存在");
//}
return appletLoginService.appletLogin(loginCode);
}
/**
* 根据openid判断当前用户信息是否存在微信小程序登录页使用
*
* @param openId 微信用户的openid
* @return 标识LOGIN已登录NOT_LOGIN未登录
*/
@GetMapping("/existUserInfo")
public AjaxResult existUserInfo(String openId) {
if (StringUtils.isBlank(openId)) {
return AjaxResult.error("用户微信唯一标识不存在!");
}
return appletLoginService.existUserInfo(openId);
}
}

View File

@ -0,0 +1,43 @@
package com.yf.exam.modules.applet.entity;
import com.alibaba.fastjson2.annotation.JSONField;
import java.io.Serializable;
import lombok.Data;
/**
* @Description 微信小程序accessToken信息实体类
* @Author 纪寒
* @Date 2022-08-17 17:05:10
* @Version 1.0
*/
@Data
public class AppletAccessToken implements Serializable {
private static final long serialVersionUID = 7351002130502802480L;
/**
* accessToken值
*/
@JSONField(name = "access_token")
private String accessToken;
/**
* access_token有效时间有效时间为7200秒
*/
@JSONField(name = "expires_in")
private String expiresIn;
/**
* 错误状态码
* 40001AppSecret 错误或者 AppSecret 不属于这个小程序请开发者确认 AppSecret 的正确性
* 40002请确保 grant_type 字段值为 client_credential
* 40013不合法的 AppID请开发者检查 AppID 的正确性避免异常字符注意大小写
*/
private Integer errcode;
/**
* 错误状态值
* 40001AppSecret 错误或者 AppSecret 不属于这个小程序请开发者确认 AppSecret 的正确性
* 40002请确保 grant_type 字段值为 client_credential
* 40013不合法的 AppID请开发者检查 AppID 的正确性避免异常字符注意大小写
*/
private String errmsg;
}

View File

@ -0,0 +1,27 @@
package com.yf.exam.modules.applet.service;
import com.yf.exam.core.domain.AjaxResult;
/**
* @description: 微信小程序登录注册业务层
* @author: haown
* @create: 2025-08-11 16:05
**/
public interface AppletLoginService {
/**
* 根据登录凭证获取用户的登录信息
*
* @param loginCode 登录凭证
* @return 微信用户登录信息
*/
AjaxResult appletLogin(String loginCode);
/**
* 根据openid判断当前用户信息是否存在微信小程序登录页使用
*
* @param openId 微信用户的openid
* @return 用户列表
*/
AjaxResult existUserInfo(String openId);
}

View File

@ -0,0 +1,147 @@
package com.yf.exam.modules.applet.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yf.exam.config.AppletChatConfig;
import com.yf.exam.constant.Constants;
import com.yf.exam.core.domain.AjaxResult;
import com.yf.exam.core.utils.AppletChatUtil;
import com.yf.exam.modules.applet.service.AppletLoginService;
import com.yf.exam.modules.applet.vo.AppletLoginVO;
import com.yf.exam.modules.sys.user.entity.SysUser;
import com.yf.exam.modules.sys.user.service.SysUserService;
import com.yf.exam.modules.utils.AppletAccessTokenUtil;
import java.util.Date;
import java.util.Objects;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
/**
* @Description 微信小程序登录注册业务层实现类
* @Author 纪寒
* @Date 2022-09-02 10:55:29
* @Version 1.0
*/
@Service
@Slf4j
public class AppletLoginServiceImpl implements AppletLoginService {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Resource
private AppletChatConfig appletChatConfig;
@Resource
private AppletAccessTokenUtil appletAccessTokenUtil;
@Resource
private SysUserService sysUserService;
/**
* 返回成功状态码
*/
private static final int SUCCESS_CODE = 0;
/**
* 返回成功状态码
*/
private static final String OK = "ok";
/**
* 获取微信小程序access_token错误码
*/
private static final int ERROR_ACCESS_CODE = 40001;
/**
* 根据登录凭证获取用户的登录信息
*
* @param loginCode 登录凭证
* @return 微信用户登录信息
*/
@Override
public AjaxResult appletLogin(String loginCode) {
//根据code获取用户的微信unionId以及openId等信息
AppletLoginVO appletLoginInfo = AppletChatUtil.getAppletLoginInfo(appletChatConfig.getAppletId(), appletChatConfig.getSecret(), loginCode, appletChatConfig.getGrantType());
if (Objects.isNull(appletLoginInfo)) {
return AjaxResult.error("获取微信小程序用户信息失败");
}
if (Objects.nonNull(appletLoginInfo.getErrcode()) && appletLoginInfo.getErrcode() != SUCCESS_CODE) {
return AjaxResult.error("获取微信小程序用户信息失败,失败信息为:" + appletLoginInfo.getErrmsg());
}
//获取微信accessToken
String accessToken;
String accessTokenKey = Constants.EXAM_APPLET_ACCESS_TOKEN + "accessToken";
//从Redis中取出accessToken
Object object = redisTemplate.opsForValue().get(accessTokenKey);
if (Objects.isNull(object)) {
//没有获取accessToken
accessToken = appletAccessTokenUtil.getAppletAccessToken();
} else {
accessToken = (String) object;
}
//获取用户手机号
//AppletPhoneVO appletPhoneInfo = AppletChatUtil.getAppletPhoneInfo(phoneCode, accessToken);
//if (Objects.isNull(appletPhoneInfo)) {
// return AjaxResult.error("获取用户手机号失败");
//}
//if (Objects.nonNull(appletPhoneInfo.getErrcode()) && appletPhoneInfo.getErrcode() == ERROR_ACCESS_CODE) {
// //当前Redis缓存中的access_token无效直接删除
// if (Objects.nonNull(object)) {
// redisTemplate.delete(accessTokenKey);
// //删除之后重新获取获取accessToken
// accessToken = appletAccessTokenUtil.getAppletAccessToken();
// appletPhoneInfo = AppletChatUtil.getAppletPhoneInfo(phoneCode, accessToken);
// if (Objects.isNull(appletPhoneInfo)) {
// return AjaxResult.error("获取用户手机号失败");
// }
// if (Objects.nonNull(appletPhoneInfo.getErrcode()) && appletPhoneInfo.getErrcode() == ERROR_ACCESS_CODE) {
// return AjaxResult.error("登录失败!");
// }
// }
//}
//if (StringUtils.isNotBlank(appletPhoneInfo.getErrmsg()) && !OK.equals(appletPhoneInfo.getErrmsg())) {
// return AjaxResult.error("获取用户手机号失败,失败信息为:" + appletPhoneInfo.getErrmsg());
//}
// 根据手机号和openid判断当前用户是否存在
//String phone = StringUtils.isBlank(appletPhoneInfo.getPhoneInfo().getPhoneNumber()) ? "" : appletPhoneInfo.getPhoneInfo().getPhoneNumber();
String openId = StringUtils.isBlank(appletLoginInfo.getOpenid()) ? "" : appletLoginInfo.getOpenid();
QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
wrapper.lambda()
//.eq(SysUser::getPhone, phone)
.eq(SysUser::getOpenid, openId);
SysUser userByPhone = sysUserService.getOne(wrapper, false);
SysUser user = new SysUser();
// 考生信息为空返回openId
if (Objects.isNull(userByPhone)) {
return AjaxResult.success("您还未注册,请注册后使用!", openId);
}
//更新用户的openid等微信标识信息
user.setId(userByPhone.getId());
user.setOpenid(openId);
user.setPhone(StringUtils.isBlank(userByPhone.getPhone()) ? "" : userByPhone.getPhone());
user.setUpdateTime(new Date());
sysUserService.updateById(user);
return AjaxResult.success(user);
}
/**
* 根据openid判断当前用户信息是否存在微信小程序登录页使用
*
* @param openId 微信用户的openid
* @return
*/
@Override
public AjaxResult existUserInfo(String openId) {
QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(SysUser::getOpenid, openId);
SysUser user = sysUserService.getOne(wrapper, false);
if (Objects.nonNull(user) && StringUtils.isNotBlank(user.getOpenid())) {
return AjaxResult.success("LOGIN");
}
return AjaxResult.success("NOT_LOGIN");
}
}

View File

@ -0,0 +1,39 @@
package com.yf.exam.modules.applet.vo;
import java.io.Serializable;
import lombok.Data;
/**
* @Description 微信小程序用户信息实体类
* @Author 纪寒
* @Date 2022-08-17 16:12:22
* @Version 1.0
*/
@Data
public class AppletLoginVO implements Serializable {
private static final long serialVersionUID = 3407837292456224369L;
/**
* 小程序unionid
*/
private String unionid;
/**
* 小程序openid
*/
private String openid;
/**
* 错误状态码40029js_code无效45011API 调用太频繁请稍候再试
* 40226高风险等级用户小程序登录拦截 -1系统繁忙此时请开发者稍候再试
*/
private Integer errcode;
/**
* 状态信息取值有40029code 无效
* 45011api minute-quota reach limit mustslower retry next minute
* 40226code blocked
* -1system error
*/
private String errmsg;
}

View File

@ -0,0 +1,85 @@
package com.yf.exam.modules.applet.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Description 获取小程序用户手机号实体类
* @Author 纪寒
* @Date 2022-08-17 17:44:53
* @Version 1.0
*/
@NoArgsConstructor
@Data
public class AppletPhoneVO implements Serializable {
private static final long serialVersionUID = -135523900524315866L;
/**
* 错误编码
* 0成功
* -1系统繁忙此时请开发者稍候再试
* 40029不合法的codecode不存在已过期或者使用过
*/
@JsonProperty("errcode")
private Integer errcode;
/**
* 返回提示信息ok成功
*/
@JsonProperty("errmsg")
private String errmsg;
/**
* 电话信息实体类
*/
@JsonProperty("phone_info")
private PhoneInfoDTO phoneInfo;
@NoArgsConstructor
@Data
public static class PhoneInfoDTO {
/**
* 用户绑定的手机号国外手机号会有区号
*/
@JsonProperty("phoneNumber")
private String phoneNumber;
/**
* 没有区号的手机号
*/
@JsonProperty("purePhoneNumber")
private String purePhoneNumber;
/**
* 区号
*/
@JsonProperty("countryCode")
private Integer countryCode;
/**
* 数据水印
*/
@JsonProperty("watermark")
private WatermarkDTO watermark;
@NoArgsConstructor
@Data
public static class WatermarkDTO {
/**
* 用户获取手机号操作的时间戳
*/
@JsonProperty("timestamp")
private Integer timestamp;
/**
* 小程序appid
*/
@JsonProperty("appid")
private String appid;
}
}
}

View File

@ -55,7 +55,7 @@ public class SysUserController extends BaseController {
}
/**
* 用户登录
* 用户退出
* @return
*/
@CrossOrigin

View File

@ -40,6 +40,9 @@ public class SysUserDTO extends UserAttachment implements Serializable {
@ApiModelProperty(value = "密码盐", required=true)
private String salt;
@ApiModelProperty(value = "用户微信openid", required=true)
private String openid;
@ApiModelProperty(value = "角色列表", required=true)
private String roleIds;

View File

@ -25,5 +25,8 @@ public class SysUserLoginReqDTO implements Serializable {
@ApiModelProperty(value = "密码", required=true)
private String password;
@ApiModelProperty(value = "用户微信openid", required=true)
private String openid;
}

View File

@ -35,6 +35,9 @@ public class SysUserLoginDTO extends UserAttachment implements Serializable {
@ApiModelProperty(value = "手机号", required=true)
private String phone;
@ApiModelProperty(value = "用户微信openid", required=true)
private String openid;
@ApiModelProperty(value = "角色列表", required=true)
private String roleIds;

View File

@ -68,6 +68,11 @@ public class SysUser extends UserAttachment {
@TableField("depart_id")
private String departId;
/**
* 用户微信openid
*/
private String openid;
/**
* 创建时间
*/

View File

@ -142,8 +142,6 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
@Override
public void update(SysUserDTO reqDTO) {
String pass = reqDTO.getPassword();
if(!StringUtils.isBlank(pass)){
PassInfo passInfo = PassHandler.buildPassword(pass);

View File

@ -0,0 +1,64 @@
package com.yf.exam.modules.utils;
import com.yf.exam.config.AppletChatConfig;
import com.yf.exam.constant.Constants;
import com.yf.exam.core.exception.ServiceException;
import com.yf.exam.core.utils.AppletChatUtil;
import com.yf.exam.modules.applet.entity.AppletAccessToken;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
/**
* @Description 获取小程序AccessToken公共方法
* @Author 纪寒
* @Date 2023-02-27 18:02:05
* @Version 1.0
*/
@Component
public class AppletAccessTokenUtil {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Resource
private AppletChatConfig appletChatConfig;
/**
* 返回成功状态码
*/
private static final int SUCCESS_CODE = 0;
/**
* 获取小程序AccessToken方法
*
* @return 小程序的AccessToken
*/
public String getAppletAccessToken() {
String accessToken;
String accessTokenKey = Constants.EXAM_APPLET_ACCESS_TOKEN + "accessToken";
//从Redis中取出accessToken
Object object = redisTemplate.opsForValue().get(accessTokenKey);
if (Objects.isNull(object)) {
//没有获取accessToken
AppletAccessToken appletAccessToken = AppletChatUtil.getAppletAccessToken(appletChatConfig.getAppletId(), appletChatConfig.getSecret());
if (Objects.isNull(appletAccessToken)) {
throw new ServiceException("获取微信小程序accessToken信息失败");
}
if (Objects.nonNull(appletAccessToken.getErrcode()) && appletAccessToken.getErrcode() != SUCCESS_CODE) {
throw new ServiceException("获取微信小程序accessToken信息失败失败信息为" + appletAccessToken.getErrmsg());
}
if (StringUtils.isBlank(appletAccessToken.getAccessToken())) {
throw new ServiceException("accessToken信息为空");
}
//存入Redis中
redisTemplate.opsForValue().set(accessTokenKey, appletAccessToken.getAccessToken(), 3600, TimeUnit.SECONDS);
accessToken = appletAccessToken.getAccessToken();
} else {
accessToken = (String) object;
}
return accessToken;
}
}

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQChOQsrP6xr7VpH
LB6kMpx0wB9QXio6Fk89GwD1rFnR/8Eym6mUbv9BR7NddJ3LL1UCSQ3b1DXuDz64
/s8nwvJmIABsgBmKS7Krb3qd7ua2NL7KodjnQuj4X3NOUQ2bPh21y5sOk6cVOObM
4g2jvaPUk52y+kDXx/8IWLIms0hPqRkgCXVtTDrtgab4wABrfha3ifiC6qHuaeza
ogo9he09QfWKPlZyW/OkwON4i0mMI0lAUaMnfSJX5QBLFwZYUHgR/8H/aMRLXnOx
W/tz1bvV2SgKwpH7TalRu2Avl9t5M1lPWTbxl4vEV6em3ijqzldpIOljwJ20cL6g
BbgJm2Y9AgMBAAECggEAOaVU2gTtWLXIZtRerGUwTgp359uTi6t4b6fdIvqaLx0c
bkT8UIeTmzrQ5mSRGxp0cdm/K8/n7JPk4G6zkUsCMwPUQvdWqn1AiE3W8Ot/8LxR
T9Co5p+k/1HZv7H5hH2kT+FaMs8Wmd77n1xgdiCTmKmjZGBYmQ74oHpeULof6+NA
JX2LEMXBjSvcsEj2ppml1M9XjZzB7zPQPqVPGL5hTAqQpT179/iy8aPdzBZRKAFY
liajWYlRVMseu1S21PaOygNpRhvLj+LQB+dfQ+s5jYIhBvVjJ4mNFK+ro1KrbFEa
g2pKYFqcY9B4uOEB12K1bA4lxWPDjZvi5L71ncB8wQKBgQDTMDnOH+IQ/jrVbnX3
YM24gKoJiKzbXcGSeg5jciGpAQ2JGtKw3xbZB54yXCwvJXssXkSfKmW9qKR299pG
TloaKHbhHxvX1jECpFmvRTLEhKb6uWcy779NFv+443nM/G0cB+vxhezj4tqE6Qqw
ijbDD+QS26Dm+ZfEQTaH1DaLbQKBgQDDbq8PEsL3uaoPg8gmyKJx9hmUW2nn3Tto
CgOI7PVQbvwOIoI8phuc1VLatnxjx3SeMasGoWoVk7/hc0ZXHvo0QsQWZt74McSC
mV2ub7L6BLyvrpsufEJZlUIiQXDIHDcucszgRaYJk4rJKmCh1R7c6dewAVE/TqTW
7GrwUBI0EQKBgBRSFZ3Rz4zXCY4z3MH63JCeQL4+GnPZJ6ESgYPsHXUHlufUXuWJ
8cbcRsqNt+qpbpqsT2oJSFThf0G7Q5N0QpM1xYqP1bwE7h5U9hQ5UlM6eF6zrExo
aT3fsqd1q+ifeVgzIu7QdiTPVTtouRCXnAFU7BssauUOqx3FMJwNPVpZAoGAYMZ9
j6RFwbBB4z7prTLrJi2sywddcUDfOwzCZVqZu8PJsyIphejngYktZzq4bByHxhJo
U3c49ghdG0IfEeM4GQr62PEF9reGTPmvJ0MOyLnxyblYBPPpUz0TK61mMOGv/aNB
islSiCl0r1r50QmdJ93wParZVdUW0YrvBaNQ8FECgYAm1j0tfGsMmocMYSoeMGt5
wRetXbkrKfMbNh6GALiNClEj6tFYKMbuQOuUzADYDxHHz/SPNozuP7Ax/NI95rtw
mGZcMx/KhKZQvZkAwCWUqRSn6cDPtqH4KNta7QZ8G36XtdPRP1a/h8g96cEiB/CV
zQPps/kR9HNAJfr0sBPEWA==
-----END PRIVATE KEY-----

View File

@ -28,20 +28,69 @@ server:
enabled: true
min-response-size: 10
mime-types: application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css
# 新医路微信商户号配置参数
# redis 配置
redis:
# 地址
host: localhost
# 端口默认为6379
port: 6379
# 数据库索引
database: 2
# 密码
password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 考试系统微信小程序参数配置信息
exam-applet-chat-config:
# 微信小程序
applet-id: wx26b9ecdba54bc588
# 微信小程序密钥
secret: ccd19a0bb2bc74f38b083c902daf48a7
# 微信小程序返回国家语言
lang: zh_CN
# 微信小程序授权类型
grant-type: authorization_code
# 微信小程序事件回调令牌
token: Yw3vfW1ILpc34qAVDtTpB2hesAMCpvW0
# 山东省公共卫生学会微信商户号配置参数
xyl-we-chat-config:
# 新医路商户号 1633348407 山东新医路信息科技有限公司 山东柏杏新医健康服务有限公司
xyl-mch-id: 1690248007
# 新医路商户号API证书序号 7C6A18FC8E1F0445901B1BE1C4DD1ACE284C3D79
xyl-mch-serial-no: 760D3316C2F3DF8D1DB05B56A37BFCD3C34EEDA8
# 新医路商户私钥文件
xyl-private-key-path: baixing_apiclient_key.pem
# 新医路API V3版本密钥 Xyl699003981qazVFR4xsw23edcASDFG
xyl-payment-key: baixingXINYIHULIZHAN202400000000
# 新医路微信支付回调地址 https://quanyidaojia.xinelu.cn
# 山东省公共卫生学会商户号 1724506668 山东省公共卫生学会
xyl-mch-id: 1724506668
# 山东省公共卫生学会商户号API证书序号 2C1188B1E533743F24AA43D374118B5D4A4FABC1
xyl-mch-serial-no: 2C1188B1E533743F24AA43D374118B5D4A4FABC1
# 山东省公共卫生学会商户私钥文件
xyl-private-key-path: apiclient_key.pem
# 山东省公共卫生学会API V3版本密钥 Xyl699003981qazVFR4xsw23edcASDFG
xyl-payment-key: gonggongweishengXUEHUI2025081100
# 山东省公共卫生学会微信支付回调地址 https://quanyidaojia.xinelu.cn
xyl-wechat-notify-url: http://8.131.93.145:54097
# h5支付接口地址
# 微信支付接口地址包含小程序和App支付接口地址
we-chat-payment-url-config:
# h5下单接口地址
h5-palce-order-url: https://api.mch.weixin.qq.com/v3/pay/transactions/h5
# 小程序JSAPI下单接口地址
jsapi-palce-order-url: https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi
# 微信支付订单号查询接口地址
query-order-no-url: https://api.mch.weixin.qq.com/v3/pay/transactions/id/%s
# 商户订单号接口查询
query-mch-id-url: https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s
# 关闭订单接口地址
close-order-url: https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s/close
# 申请退款接口地址
refund-apply-url: https://api.mch.weixin.qq.com/v3/refund/domestic/refunds
# 查询单笔退款接口地址
refund-query-order-url: https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/%s
# 申请交易账单接口地址
trade-apply-bill-url: https://api.mch.weixin.qq.com/v3/bill/tradebill
# 申请资金账单接口地址
capital-apply-bill-url: https://api.mch.weixin.qq.com/v3/bill/fundflowbill
# App下单接口地址
app-place-order-url: https://api.mch.weixin.qq.com/v3/pay/transactions/app

View File

@ -29,13 +29,14 @@
<result column="photo" property="photo" />
<result column="certificate" property="certificate" />
<result column="physical_report" property="physicalReport" />
<result column="openid" property="openid" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
`id`,`user_name`,`real_name`,phone,`password`,`salt`,`role_ids`,`depart_id`,`create_time`,`update_time`,`state`,
`email`,`address`,`education`,`graduate_school`,`major`,`reg_type`,`train_institution`,`train_start_date`,`train_end_date`,
`card_front`,`card_back`,`card_copy`,`photo`,`certificate`,`physical_report`
`card_front`,`card_back`,`card_copy`,`photo`,`certificate`,`physical_report`,`openid`
</sql>