feat: add core entities, controllers, services, and mappers across multi-module project

This commit is contained in:
zhangsz committed 2026-09-20 15:03:05 +08:00
commit 4335344af7
1456 files changed
+58308

No files matched your search

View File
Whitespace-only changes.
View File
Whitespace-only changes.
+2
View File
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4" />
+167
View File
@@ -0,0 +1,167 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>AI_Questioning</artifactId>
<groupId>cn.unizk</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>applet</artifactId>
<name>applet</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>8</maven.compiler.release>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
<version>2.6.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-jackson -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.17</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk18on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
</dependency>
<dependency>
<groupId>com.huifu.bspay.sdk</groupId>
<artifactId>dg-java-sdk</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.java-websocket/Java-WebSocket -->
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.squareup.okio/okio -->
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
</dependency>
<dependency>
<groupId>com.volcengine</groupId>
<artifactId>volcengine-java-sdk-ark-runtime</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>cn.unizk</groupId>
<artifactId>common</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.1.RELEASE</version>
<configuration>
<fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,16 @@
package cn.unizk;
import cn.unizk.config.WxProperties;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@MapperScan(basePackages = {"cn.unizk.mapper"})
@EnableScheduling
public class AIQuestioningApp {
public static void main(String[] args) {
SpringApplication.run(AIQuestioningApp.class,args);
}
}
@@ -0,0 +1,50 @@
/*
package cn.unizk.config;
import cn.unizk.filter.JkCorsFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import java.util.ArrayList;
import java.util.List;
*/
/**
* @Author hao
* @Date 2025/9/13 17:09
* @Version 1.0
**//*
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<JkCorsFilter> jkCorsFilter(){
FilterRegistrationBean<JkCorsFilter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new JkCorsFilter());
filterRegistrationBean.addUrlPatterns("/h5/*");
filterRegistrationBean.setOrder(1);
return filterRegistrationBean;
}
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("http://369.peaktcm.com:9000");
config.addExposedHeader("Authorization");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
*/
@@ -0,0 +1,22 @@
package cn.unizk.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @Author hao
* @Date 2025/8/27 10:18
* @Version 1.0
**/
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "jk")
public class JkProperties {
private String secretKey;
private Integer tokenExpireTime;
}
@@ -0,0 +1,58 @@
package cn.unizk.config;
import cn.unizk.interceptor.AppletLoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
/**
* @Author hao
* @Date 2025/8/21 14:52
* @Version 1.0
**/
@Configuration
public class ResourceConfig implements WebMvcConfigurer {
@Resource
private AppletLoginInterceptor appletLoginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(appletLoginInterceptor).addPathPatterns("/**")
.excludePathPatterns(
"/ai/saveConversation",
"/applet/login",
"/applet/jkLogin",
"/applet/sendPhoneCode",
"/applet/bindPhoneNumber",
"/applet/wxPay/paySuccessCallBack",
"/applet/test/**",
"/h5/**",
"/applet/dict/getDictByType",
"/applet/product/carousel",
"/applet/product/list",
"/applet/product/getExquisite",
"/applet/getStores",
"/applet/getStoreById",
"/applet/huifuPay/callback",
"/applet/huifuPay/configureAppletCallback",
"/applet/getAppletQrCode",
"/hf/webhook/**",
"/ai/getQustion/**",
"applet/getReportById/**",
"/applet/user/rememberDia",
"/applet/product/byId",
"/applet/combo/getCombos",
"/ai/**",
"/applet/doubao/**",
"/applet/golf/**",
"/applet/wechat/pay",
"/applet/module/**",
"/applet/loginByPhone",
"/applet/sendPhoneCode",
"/applet/oneClickLogin");
}
}
@@ -0,0 +1,43 @@
package cn.unizk.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @Author hao
* @Date 2025/7/9 17:14
* @Version 1.0
**/
@Component
@ConfigurationProperties(prefix = "wx")
@Getter
@Setter
public class WxProperties {
private String appid;
private String secret;
private final PlaceOrder placeOrder = new PlaceOrder();
private final Pay pay = new Pay();
@Getter
@Setter
public static class PlaceOrder {
private String notifyUrl;
private String placeOrderUrl;
private Long expireTime; //单位分钟
}
@Getter
@Setter
public static class Pay {
private String domain;
private String mchid;
private String serialNo;
private String closeOrderApi;
private String platformCertSerial;
private String apiV3Key;
private String orderSearchUri;
}
}
@@ -0,0 +1,16 @@
package cn.unizk.constant;
/**
* @Author hao
* @Date 2025/7/9 13:45
* @Version 1.0
**/
public class WxConstant {
public static final String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/stable_token";
public static final String APPLET_LOGIN_URL = "https://api.weixin.qq.com/sns/jscode2session";
public static final String APPLET_LOGIN_SIGN = "applet-auth";
public static final String CACHE_TOKEN_KEY = "jk-applet-";
public static final String authorization_type = "WECHATPAY2-SHA256-RSA2048 ";
public static final String WX_ACCESS_TOKEN = "access_token";
}
@@ -0,0 +1,517 @@
package cn.unizk.controller;
import cn.unizk.cache.LocalCache;
import cn.unizk.entity.po.*;
import cn.unizk.entity.vo.*;
import cn.unizk.enums.ContentTypeEnum;
import cn.unizk.exception.ServiceException;
import cn.unizk.mapper.DiagnosisTemplateMapper;
import cn.unizk.mapper.StoreAccountMapper;
import cn.unizk.mapper.TreatParameterMapper;
import cn.unizk.service.*;
import cn.unizk.utils.IatModelZh;
import cn.unizk.utils.WebIATWS;
import cn.unizk.utils.XfSpeechToTextUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.WebSocket;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.security.SignatureException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* @Author hao
* @Date 2025/7/7 15:55
* @Version 1.0
**/
@RestController
@Slf4j
@RequestMapping({"/ai"})
public class AIAdviceController {
@Value("${xf.appid}")
private String appid;
@Value("${xf.long-sound.keySecret}")
private String longSoundKeySecret;
@Value("${xf.instant.host}")
private String hostUrl;
@Value("${xf.instant.APISecret}")
private String apiSecret;
@Value("${xf.instant.apiKey}")
private String apiKey;
@Resource
private WxUserSessionService userSessionService;
@Resource
private QuestionService questionService;
@Resource
private StoreAccountMapper accountMapper;
@Resource
private AIAdviceService aiAdviceService;
@Value("${spring.profiles.active}")
private String active;
@Resource
private DiagnosisTemplateMapper diagnosisTemplateMapper;
@Resource
private TreatParameterMapper treatParameterMapper;
@Resource
private SysDictService sysDictService;
@PostMapping("/askForAdvice")
public ReplyVo reply(@RequestBody ReplyVo vo){
/**
* 如果下一条消息要展示图片,那么vo.setNextContentType(ContentTypeEnum.IMAGE.name());
* 如果下一条消息要弹出性别选择,那么vo.setNextContentType(ContentTypeEnum.SEX.name());
* 如果下一条消息要弹出年龄选择,那么vo.setNextContentType(ContentTypeEnum.AGE.name());
* 如果下一条消息要弹出体重选择,那么vo.setNextContentType(ContentTypeEnum.WEIGHT.name());
*/
vo.setType("AI");
return aiAdviceService.reply(vo);
}
@PostMapping("/saveConversation")
public ReplyVo saveConversation(@RequestBody List<Map<String, List<String>>> conversation,@RequestParam("openid") String openid){
// List<Map<String,String>> maps = new ArrayList<>();
// for (Map<String, List<String>> stringListMap : conversation) {
// for (Map.Entry<String, List<String>> stringListEntry : stringListMap.entrySet()) {
// List<String> value = stringListEntry.getValue();
// Map<String,String> map = new HashMap<>();
// map.put(stringListEntry.getKey(),value.get(0));
// maps.add(map);
// }
// }
return aiAdviceService.saveConversation(conversation,openid);
}
/*
*
* 获取诊疗报告 诊疗师,暂放
* */
@PostMapping("/getTreatmentPlan")
public JSONObject getTreatmentPlan(@RequestBody String sessionId){
return aiAdviceService.getTreatmentPlan(sessionId);
}
@GetMapping("/getQustion/{sort}")
public QuestionVo getQuestion(@PathVariable("sort") int sort){
Question question = questionService.getOne(new LambdaQueryWrapper<Question>().eq(Question::getSort, sort));
JSONObject options = JSONObject.parseObject(question.getOptions());
List<Map<String,String>> optionValues = new ArrayList<>();
options.forEach((k,v) -> {
Map<String,String> item = new HashMap<>();
item.put("value", (String) v);
item.put("text", (String) v);
optionValues.add(item);
});
QuestionVo questionVo = new QuestionVo();
BeanUtils.copyProperties(question,questionVo);
questionVo.setOptionValues(optionValues);
return questionVo;
}
@PostMapping("/speechToText")
public String speechToText(@RequestParam("file") MultipartFile file) {
log.info("originalFilename:{}",file.getOriginalFilename());
log.info("name={},size={}",file.getName(),file.getSize());
try {
String s = XfSpeechToTextUtil.xfSpeechToText(file, appid, longSoundKeySecret);
JSONObject res = JSONObject.parseObject(s);
String orderId = res.getJSONObject("content").getString("orderId");
String result = XfSpeechToTextUtil.getResult(orderId, appid, longSoundKeySecret);
String parseStr = newJavaLfasr(result).toString();
log.info("解析后的内容:{}",parseStr);
return parseStr;
} catch (SignatureException | InterruptedException | IOException e) {
e.printStackTrace();
throw new ServiceException("语音识别失败");
}
}
@PostMapping("/instantToText/{openid}")
public String instantToText(@RequestParam("file") MultipartFile file,@PathVariable("openid") String openid){
try {
String authUrl = WebIATWS.getAuthUrl(hostUrl, apiKey, apiSecret);
OkHttpClient client = new OkHttpClient.Builder().build();
//将url中的 schema http://和https://分别替换为ws:// 和 wss://
String url = authUrl.toString().replace("http://", "ws://").replace("https://", "wss://");
//System.out.println(url);
Request request = new Request.Builder().url(url).build();
// System.out.println(client.newCall(request).execute());
//System.out.println("url===>" + url);
WebSocket webSocket = client.newWebSocket(request, new WebIATWS(file,openid));
long end = System.currentTimeMillis() + 5 * 1000;
while (true){
String msg = LocalCache.MESSAGE_QUEUE.get(openid);
if (StringUtils.isNotBlank(msg)){
LocalCache.MESSAGE_QUEUE.remove(openid);
return msg;
}else {
if (System.currentTimeMillis() > end){
throw new ServiceException("翻译结果等待超时");
}
log.info("等待翻译结果返回");
Thread.sleep(1000);
}
}
}catch (Exception e){
e.printStackTrace();
throw new ServiceException("语音识别失败");
}
}
/**
* 中文识别大模型API
* @param file
* @param openid
* @return
*/
@PostMapping("/iatModelToText")
public String iatModelToText(@RequestParam("file") MultipartFile file,@RequestParam("openid") String openid){
/*try {
String authUrl = IatModelZh.getAuthUrl();
OkHttpClient client = new OkHttpClient.Builder().build();
String url = authUrl.toString().replace("http://", "ws://").replace("https://", "wss://");
Request request = new Request.Builder().url(url).build();
InputStream inputStream = file.getInputStream();
IatModelZh iatModelZh = new IatModelZh(file, openid);
WebSocket webSocket = client.newWebSocket(request, iatModelZh);
if (!iatModelZh.getLatch().await(10, TimeUnit.SECONDS)) {
throw new ServiceException("大模型听写 超时");
}
String s = LocalCache.IAT_MODEL_MSG_QUEUE.get(openid);
LocalCache.IAT_MODEL_MSG_QUEUE.remove(openid);
return s;
}catch (Exception e){
log.error("大模型语音识别失败",e);
throw new ServiceException("大模型语音识别失败");
}*/
return instantToText(file,openid);
}
private static StringBuilder newJavaLfasr(String a){
StringBuilder result= new StringBuilder();
JSONObject jsonObject = JSON.parseObject(a);
String str = jsonObject.getJSONObject("content").getString("orderResult");
JSONArray json = JSONObject.parseObject(str).getJSONArray("lattice2");
int length = json.size();
for(int i = 0;i<length;i++) {
int length2 = json.getJSONObject(i).getJSONObject("json_1best").getJSONObject("st").getJSONArray("rt").size();
for(int j=0;j<length2;j++){
int length3 =json.getJSONObject(i).getJSONObject("json_1best").getJSONObject("st").getJSONArray("rt").getJSONObject(j).getJSONArray("ws").size();
for(int k=0;k<length3;k++){
String resultTemp = json.getJSONObject(i).getJSONObject("json_1best").getJSONObject("st").getJSONArray("rt").getJSONObject(j).getJSONArray("ws").getJSONObject(k).getJSONArray("cw").getJSONObject(0).getString("w");
result.append(resultTemp);
}
}
}
return result;
}
@PostMapping("/saveSession/{openid}")
public ResultData saveSession(@RequestBody List<WxUserSession> sessions, @PathVariable("openid") String openid){
Long maxSessionId = userSessionService.findMaxSessionId(openid);
sessions.forEach(userSession -> {
userSession.setOpenid(openid);
// userSession.setSessionId(maxSessionId == null ? 1 : maxSessionId+1);
userSession.setSessionTime(LocalDateTime.now());
});
userSessionService.saveBatch(sessions);
return ResultData.success();
}
@PostMapping("/manualAnswer")
public ResultData<String> manualAnswer(@RequestBody ManualAnswerVO vo){
ResultData<String> data = new ResultData<>();
data.setCode(200);
if (Objects.isNull(vo) || StringUtils.isBlank(vo.getMessage())){
return ResultData.success();
}
String message = vo.getMessage();
if ("1".equals(vo.getNumber())){
if (message.contains("膝")){
data.setData("true");
return data;
}
data.setData("false");
return data;
}else if ("4".equals(vo.getNumber()) || "2".equals(vo.getNumber())){
if (message.contains("不对")){
data.setData("false");
return data;
}
if (message.contains("不是")){
data.setData("false");
return data;
}
if (message.contains("对")){
data.setData("true");
return data;
}
if (message.contains("是")){
data.setData("true");
return data;
}
data.setData("false");
return data;
}else if ("3".equals(vo.getNumber())){
message = parseStrTen(message);
message = message.replace("和",",").replace("还有",",").replace("跟",",").replace("号",",").replace("区",",");
List<String> list = new ArrayList<>();
if (containsStandaloneOne(message)){
list.add("1");
}
if (containsStandaloneTwo(message)){
list.add("2");
}
if (containsStandaloneThree(message)){
list.add("3");
}
if (message.contains("4")){
list.add("4");
}
if (message.contains("5")){
list.add("5");
}
if (message.contains("6")){
list.add("6");
}
if ( message.contains("7")){
list.add("7");
}
if (message.contains("8")){
list.add("8");
}
if (message.contains("9")){
list.add("9");
}
if (message.contains("10")){
list.add("10");
}
if (message.contains("11")){
list.add("11");
}
if (message.contains("12")){
list.add("12");
}
if (message.contains("13")){
list.add("13");
}
if (CollectionUtils.isNotEmpty(list)){
String collect = String.join(",", list);
data.setData(collect);
return data;
}else {
data.setData("false");
return data;
}
}else {
List<String> list = new ArrayList<>();
if (message.contains("一") || message.contains("1")){
list.add("1");
}
if (message.contains("二") || message.contains("2")){
list.add("2");
}
if (message.contains("三") || message.contains("3")){
list.add("3");
}
if (message.contains("四") || message.contains("4")){
list.add("4");
}
if (message.contains("五") || message.contains("5")){
list.add("5");
}
if (message.contains("六") || message.contains("6")){
list.add("6");
}
if (CollectionUtils.isNotEmpty(list)){
String join = String.join(",", list);
data.setData(join);
return data;
}else {
data.setData("false");
return data;
}
}
}
@PostMapping("/answer")
public ResultData<String> answer(@RequestBody QuestionAnswerVO vo){
ResultData<String> data = new ResultData<>();
data.setCode(200);
return data;
}
private boolean containsStandaloneOne(String str) {
if (str == null) {
return false;
}
return Pattern.compile("\\b1\\b").matcher(str).find();
}
private boolean containsStandaloneTwo(String str) {
if (str == null) {
return false;
}
return Pattern.compile("\\b2\\b").matcher(str).find();
}
private boolean containsStandaloneThree(String str) {
if (str == null) {
return false;
}
return Pattern.compile("\\b3\\b").matcher(str).find();
}
private String parseStrTen(String msg){
if (msg == null){
return msg;
}
String s = msg;
if (msg.contains("十一") || msg.contains("十二") || msg.contains("十三")){
s = msg.replace("十一", "11").replace("十二", "12").replace("十三", "13");
}else {
if (msg.contains("十")){
s = msg.replace("十","10");
}
}
if (msg.contains("一")){
s = s.replace("一","1");
}
if (msg.contains("二")){
s = s.replace("二","2");
}
if (msg.contains("三")){
s = s.replace("三","3");
}
if (msg.contains("四")){
s = s.replace("四","4");
}
if (msg.contains("五")){
s = s.replace("五","5");
}
if (msg.contains("六")){
s = s.replace("六","6");
}
if (msg.contains("七")){
s = s.replace("七","7");
}
if (msg.contains("八")){
s = s.replace("八","8");
}
if (msg.contains("九")){
s = s.replace("九","9");
}
return s;
}
@GetMapping("/toVoice")
public ResponseEntity<InputStreamResource> toVoice(@RequestParam("text") String text){
SysDict sysDict = sysDictService.getOne(new LambdaQueryWrapper<SysDict>().eq(SysDict::getDictType, "xf_voice"));
if (sysDict == null || "0".equals(sysDict.getDictValue())){
return ResponseEntity.ok(null);
}
File tempFile = null;
File dir;
try {
if ("dev".equals(active)){
dir = new File("D:\\code\\temp");
}else {
dir = new File("/opt/jkzyjava/temp");
}
dir.mkdirs();
tempFile = File.createTempFile("temp", ".mp3",dir);
String filePath = tempFile.getAbsolutePath();
CountDownLatch latch = new CountDownLatch(1);
WebTtsWsService.tts(text,filePath,latch);
if (!latch.await(10, TimeUnit.SECONDS)) {
throw new ServiceException("TTS 超时");
}
byte[] audioData = Files.readAllBytes(tempFile.toPath());
InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(audioData));
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("audio/mpeg"))
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"speech.mp3\"")
.body(resource);
} catch (Exception e) {
log.error("文字转语音发生异常",e);
throw new ServiceException("服务异常");
}finally {
if (tempFile != null){
File file = tempFile;
new Thread(() -> {
try {
Thread.sleep(5000);
log.info("删除临时文件");
file.delete();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.warn("删除临时文件线程被中断");
e.printStackTrace();
}
}).start();
}
}
}
@GetMapping("/addTreat")
public boolean addTreat(){
List<DiagnosisTemplate> diagnosisTemplates = diagnosisTemplateMapper.selectList(null);
for (DiagnosisTemplate diagnosisTemplate : diagnosisTemplates) {
TreatParameter treatParameter = new TreatParameter();
treatParameter.setSymptomsName(diagnosisTemplate.getLocation());
treatParameterMapper.insert(treatParameter);
}
return true;
}
/*@GetMapping("/abcount")
public ResultData data(){
StoreAccount account = new StoreAccount();
account.setStoreId(3);
account.setAvailableBalance(BigDecimal.valueOf(1.00));
account.setBalance(BigDecimal.valueOf(1.00));
accountMapper.insert(account);
return ResultData.success();
}*/
}
@@ -0,0 +1,53 @@
package cn.unizk.controller;
import cn.unizk.constant.WxConstant;
import cn.unizk.entity.po.AppletModule;
import cn.unizk.entity.po.MobileAppUser;
import cn.unizk.entity.po.ModulePerms;
import cn.unizk.mapper.AppletModuleMapper;
import cn.unizk.mapper.MobileAppUserMapper;
import cn.unizk.utils.JkSecurityUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Objects;
/**
* @Author hao
* @Date 2026/8/6 10:27
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/module")
public class ApplicationModuleController {
@Resource
private AppletModuleMapper appletModuleMapper;
@Resource
private MobileAppUserMapper mobileAppUserMapper;
@GetMapping("/getModule")
public List<AppletModule> getModule(HttpServletRequest request){
String token = request.getHeader(WxConstant.APPLET_LOGIN_SIGN);
if (StringUtils.isBlank(token)){
return appletModuleMapper.getModule("mp","C");
}
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(token);
MobileAppUser user = mobileAppUserMapper.selectById(currentUser);
if (Objects.isNull(user)){
return appletModuleMapper.getModule("mp","C");
}
return appletModuleMapper.getModule("mp",user.getUserType());
}
/* @GetMapping("/getModule")
public List<AppletModule> getM(){
return appletModuleMapper.selectList(null);
}*/
}
@@ -0,0 +1,58 @@
package cn.unizk.controller;
import cn.unizk.entity.po.ClinicWorkLog;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.entity.vo.UniPage;
import cn.unizk.mapper.ClinicWorkLogMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang3.StringUtils;
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.RestController;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* @Author hao
* @Date 2026/8/20 14:36
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/clinic")
public class ClinicWorkLogController {
@Resource
private ClinicWorkLogMapper clinicWorkLogMapper;
@PostMapping("/list")
public Page<ClinicWorkLog> list(@RequestBody UniPage<ClinicWorkLog> uniPage){
Page<ClinicWorkLog> page = (Page<ClinicWorkLog>) uniPage.getPage();
LambdaQueryWrapper<ClinicWorkLog> queryWrapper = uniPage.buildQueryWrapper(query -> {
LambdaQueryWrapper<ClinicWorkLog> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(query.getName()), ClinicWorkLog::getName, query.getName());
wrapper.like(StringUtils.isNotBlank(query.getPhoneNumber()), ClinicWorkLog::getPhoneNumber, query.getPhoneNumber());
wrapper.orderByDesc(ClinicWorkLog::getCreateTime);
return wrapper;
});
return clinicWorkLogMapper.selectPage(page,queryWrapper);
}
@PostMapping("/addLog")
public ResultData addLog(@RequestBody ClinicWorkLog log){
if (log.getCreateTime() == null){
log.setCreateTime(LocalDate.now());
}
clinicWorkLogMapper.insert(log);
return ResultData.success();
}
@PostMapping("/editLog")
public ResultData editLog(@RequestBody ClinicWorkLog log){
clinicWorkLogMapper.updateById(log);
return ResultData.success();
}
}
@@ -0,0 +1,98 @@
package cn.unizk.controller;
import cn.unizk.constant.UniConstant;
import cn.unizk.entity.po.Combo;
import cn.unizk.entity.po.Device;
import cn.unizk.entity.vo.UniPage;
import cn.unizk.exception.ServiceException;
import cn.unizk.service.ComboService;
import cn.unizk.service.DeviceService;
import cn.unizk.utils.JkSecurityUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @Author hao
* @Date 2025/8/30 17:23
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/combo")
@Slf4j
public class ComboController {
@Resource
private ComboService comboService;
@Resource
private DeviceService deviceService;
@GetMapping("/listByDevice")
public Page<Combo> list(UniPage uniPage, @RequestParam(value = "deviceId", required = false) String deviceId, HttpServletRequest request) {
String channelId = request.getHeader(UniConstant.CHANNEL_HEADER);
log.info("从请求体中获取到的channelId={}",channelId);
Page<Combo> page = new Page<>();
page.setCurrent(uniPage.getCurrentPage());
page.setSize(uniPage.getPageSize());
if (StringUtils.isNotBlank(channelId)) {
return comboService.page(page, new LambdaQueryWrapper<Combo>()
.eq(Combo::getChannelId, channelId)
.and(w -> {
w.eq(StringUtils.isNotBlank(deviceId), Combo::getDeviceId, deviceId)
.or()
.isNull(Combo::getDeviceId)
.or()
.isNull(Combo::getChannelId);
})
.orderByAsc(Combo::getComboPrice));
}
return comboService.page(page, new LambdaQueryWrapper<Combo>()
.isNull(Combo::getChannelId)
.and(w -> {
w.eq(StringUtils.isNotBlank(deviceId), Combo::getDeviceId, deviceId)
.or()
.isNull(Combo::getDeviceId);
})
.orderByAsc(Combo::getComboPrice));
}
@GetMapping("/getCombos")
public Page<Combo> getCombos(@RequestParam("storeId") Integer storeId, @RequestParam(value = "comboName", required = false) String comboName, UniPage uniPage, HttpServletRequest request) {
/*WxUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)) {
throw new ServiceException(401, "登录已过期");
}*/
String channelId = request.getHeader("channelId");
log.info("从请求体中获取到的channelId={}",channelId);
Page<Combo> page = new Page<>(uniPage.getCurrentPage(), uniPage.getPageSize());
List<Device> devices = deviceService.list(new LambdaQueryWrapper<Device>().eq(Device::getStoreId, storeId));
if (CollectionUtils.isNotEmpty(devices)) {
List<String> deviceIds = devices.stream().map(Device::getDeviceId).collect(Collectors.toList());
return comboService.page(page, new LambdaQueryWrapper<Combo>()
.and(wrapper ->{
wrapper.in(Combo::getDeviceId, deviceIds)
.or()
.isNull(Combo::getDeviceId);
})
.like(StringUtils.isNotBlank(comboName), Combo::getComboName, comboName)
.and(wrapper -> {
wrapper.eq(StringUtils.isNotBlank(channelId), Combo::getChannelId, channelId)
.or()
.isNull(Combo::getChannelId);
}));
}
page.setRecords(new ArrayList<>());
return page;
}
}
@@ -0,0 +1,267 @@
package cn.unizk.controller;
import cn.hutool.core.date.DateUtil;
import cn.unizk.entity.dto.ElectronicSignatureDTO;
import cn.unizk.entity.po.*;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.entity.vo.UniPage;
import cn.unizk.entity.vo.UserConsultationRecordVO;
import cn.unizk.exception.ServiceException;
import cn.unizk.mapper.*;
import cn.unizk.service.impl.DoubaoServiceImpl;
import cn.unizk.utils.JkSecurityUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @Author hao
* @Date 2026/8/18 11:22
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/consultation")
@Slf4j
public class ConsultationController {
@Resource
private UserConsultationRecordMapper consultationRecordMapper;
@Resource
private DoubaoServiceImpl doubaoService;
@Resource
private TreatParameterMapper treatParameterMapper;
@Resource
private MobileAppUserMapper mobileAppUserMapper;
@Resource
private JkPhysioMapper jkPhysioMapper;
@Resource
private ClinicWorkLogMapper clinicWorkLogMapper;
@PostMapping("/electronicSignature")
public ResultData electronicSignature(@RequestBody ElectronicSignatureDTO dto){
UserConsultationRecord consultationRecord = consultationRecordMapper.selectById(dto.getId());
if ("electronicPrescriptionUrl".equals(dto.getName())){
consultationRecord.setElectronicPrescriptionUrl(dto.getUrl());
}
if ("coordinatorSignUrl".equals(dto.getName())){
consultationRecord.setCoordinatorSignUrl(dto.getUrl());
}
if ("proofreaderSignUrl".equals(dto.getName())){
consultationRecord.setProofreaderSignUrl(dto.getUrl());
}
consultationRecordMapper.updateById(consultationRecord);
return ResultData.success();
}
@PostMapping("/saveRecord")
public UserConsultationRecordVO saveRecord(HttpServletRequest request, @RequestBody UserConsultationRecordVO vo){
if (vo.getPattern() == null){
throw new ServiceException("pattern???");
}
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)){
throw new ServiceException(401,"登录已过期");
}
TreatParameter treatParameter = null;
StringBuilder part = new StringBuilder();
StringBuilder syName = new StringBuilder();
if (!vo.getPainAreaNo().contains(",")){
treatParameter = treatParameterMapper.selectById(Integer.parseInt(vo.getPainAreaNo()));
part.append(treatParameter.getPart());
syName.append(treatParameter.getSymptomsName());
}else {
String[] split = vo.getPainAreaNo().split(",");
for (String s : split) {
TreatParameter parameter = treatParameterMapper.selectById(Integer.parseInt(s));
part.append(parameter.getPart()).append(";");
syName.append(parameter.getSymptomsName()).append(";");
}
}
String s = RandomStringUtils.randomNumeric(10);
String yyyyMMdd = DateUtil.format(new Date(), "yyyyMMdd");
UserConsultationRecord consultationRecord = new UserConsultationRecord();
BeanUtils.copyProperties(vo,consultationRecord);
String number = "JK-" + yyyyMMdd + s;
consultationRecord.setBatchNumber(number);
consultationRecord.setUserId(currentUser.getId());
consultationRecord.setPainArea(part.toString());
Map<Integer, String> cqs = vo.getCqs();
String one = cqs.get(11);
String two = cqs.get(12);
String three = cqs.get(13);
String four = cqs.get(14);
StringBuilder cb = new StringBuilder();
if (vo.getPattern() == 1){
if (one.equals("怕冷,遇冷加重,热敷舒服") || two.equals("膝盖沉重肿胀,阴天积液加重")){
cb.append("寒湿痹阻型,");
}
if (one.equals("怕热,局部发热,冷敷舒服")){
cb.append("湿热瘀阻型,");
}
if (three.equals("走路、干活后疼得厉害,休息减轻") || four.equals("腰部腿脚无酸软乏力")){
cb.append("筋脉瘀滞劳损型,");
}
if (four.equals("经常腰酸腿软,上下楼无力打软腿") && (vo.getPainDuration().equals("一年以上") || vo.getPainDuration().equals("三年以上"))){
cb.append("肝肾亏虚型");
}
}else {
String input1 = "患者回答:" + one;
String input2 = "请判断患者回答的内容意思如果是怕冷,遇冷加重,热敷舒服则回复yes;如果是怕热,局部发热,冷敷舒服则回复no;如果都不是则回复false。不要回复其他多余文字";
String drawConclusion = doubaoService.drawConclusion(input1, input2);
if (drawConclusion.equals("yes")){
cb.append("寒湿痹阻型,");
}
if (drawConclusion.equals("no")){
cb.append("湿热瘀阻型,");
}
input1 = "患者回答:" + two;
input2 = "请判断患者回答的内容意思如果是膝盖沉重肿胀,阴天积液加重则回复yes;如果不是则回复no。不要回复其他多余文字";
String yy = doubaoService.drawConclusion(input1, input2);
if (yy.equals("yes")){
cb.append("寒湿痹阻型,");
}
input1 = "患者回答:" + three;
input2 = "请判断患者回答的内容意思如果是走路、干活后疼得厉害,休息减轻则回复yes;如果不是则回复no。不要回复其他多余文字";
String a = doubaoService.drawConclusion(input1, input2);
if (a.equals("yes")){
cb.append("筋脉瘀滞劳损型,");
}
input1 = "患者回答:" + four;
input2 = "请判断患者回答的内容意思如果是腰部腿脚无酸软乏力则回复yes;如果是经常腰酸腿软,上下楼无力打软腿则回复no;如果都不是则回复false。不要回复其他多余文字";
String n = doubaoService.drawConclusion(input1, input2);
if (n.equals("yes")){
cb.append("筋脉瘀滞劳损型,");
}
if (n.equals("no")){
cb.append("肝肾亏虚型");
}
}
StringBuilder desc = new StringBuilder();
StringBuilder append = desc.append("患者姓名:").append(vo.getPatientName()).append(",").append("病因:").append(syName)
.append(",").append("疼痛等级:").append(vo.getLevel()).append(",").append("诱发动作:").append(vo.getInducedAction())
.append(",").append("伴随症状:").append(vo.getSymptoms()).append(",").append("中医诊断:").append(cb);
String input2 = "上述是患者的病痛信息,请你给出禁忌与注意事项,50字左右";
String taboo = doubaoService.drawConclusion(append.toString(), input2);
MobileAppUser user = mobileAppUserMapper.selectById(currentUser.getId());
log.info("taboo:{}",taboo);
consultationRecord.setTaboo(taboo);
String drugPrice = getDrugPrice();
consultationRecord.setDrugPrice(drugPrice);
consultationRecord.setChineseDiagnosis(cb.toString());
consultationRecord.setWesternDiagnosis(syName.toString());
consultationRecord.setDepartment("中医科");
consultationRecord.setPhoneNumber(user.getPhoneNumber());
consultationRecord.setCreateTime(LocalDate.now());
consultationRecordMapper.insert(consultationRecord);
UserConsultationRecordVO recordVO = new UserConsultationRecordVO();
BeanUtils.copyProperties(consultationRecord,recordVO);
recordVO.setClinicalDia(syName.toString());
int month = DateUtil.month(new Date()) + 1;
int day = DateUtil.thisDayOfMonth();
String yyyy = DateUtil.format(new Date(), "yyyy");
String currentTime = yyyy + "年" + month + "月" + day + "日";
recordVO.setCurrentTime(currentTime);
recordVO.setDrugPrice(drugPrice);
recordVO.setGender(user.getGender());
recordVO.setAge(user.getAge());
recordVO.setPhoneNumber(currentUser.getPhoneNumber());
ClinicWorkLog clinicWorkLog = clinicWorkLogMapper.selectOne(new LambdaQueryWrapper<ClinicWorkLog>().eq(ClinicWorkLog::getPhoneNumber, user.getPhoneNumber())
.eq(ClinicWorkLog::getCreateTime, consultationRecord.getCreateTime())
.orderByDesc(ClinicWorkLog::getId)
.last("limit 1"));
if (Objects.nonNull(clinicWorkLog)){
recordVO.setMedicalTreatment(clinicWorkLog.getMedicalTreatment());
}
return recordVO;
}
@PostMapping("/getPrescriptionList")
public Page<UserConsultationRecord> getPrescriptionList(HttpServletRequest request, @RequestBody UniPage<UserConsultationRecord> uniPage){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)){
throw new ServiceException(401,"登录已过期");
}
JkPhysio jkPhysio = jkPhysioMapper.selectOne(new LambdaQueryWrapper<JkPhysio>().eq(JkPhysio::getPhysioTel, currentUser.getPhoneNumber()));
if (Objects.isNull(jkPhysio)){
throw new ServiceException("暂无理疗师身份信息!");
}
LambdaQueryWrapper<UserConsultationRecord> lambdaQueryWrapper = uniPage.buildQueryWrapper(query -> {
LambdaQueryWrapper<UserConsultationRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(StringUtils.isNotBlank(query.getPatientName()), UserConsultationRecord::getPatientName, query.getPatientName());
wrapper.orderByDesc(UserConsultationRecord::getCreateTime);
return wrapper;
});
Page<UserConsultationRecord> page = (Page<UserConsultationRecord>) uniPage.getPage();
return consultationRecordMapper.selectPage(page,lambdaQueryWrapper);
}
@GetMapping("/getPrescription")
public UserConsultationRecordVO prescription(@RequestParam("id") Integer id){
UserConsultationRecord consultationRecord = consultationRecordMapper.selectById(id);
MobileAppUser user = mobileAppUserMapper.selectById(consultationRecord.getUserId());
ClinicWorkLog clinicWorkLog = clinicWorkLogMapper.selectOne(new LambdaQueryWrapper<ClinicWorkLog>().eq(ClinicWorkLog::getPhoneNumber, consultationRecord.getPhoneNumber())
.eq(ClinicWorkLog::getCreateTime, consultationRecord.getCreateTime())
.orderByDesc(ClinicWorkLog::getId)
.last("limit 1"));
UserConsultationRecordVO vo = new UserConsultationRecordVO();
BeanUtils.copyProperties(consultationRecord,vo);
LocalDate createTime = consultationRecord.getCreateTime();
int yyyy = DateUtil.year(DateUtil.date(createTime));
int month = DateUtil.month(DateUtil.date(createTime)) + 1;
int day = DateUtil.dayOfMonth(DateUtil.date(createTime));
String currentTime = yyyy + "年" + month + "月" + day + "日";
vo.setCurrentTime(currentTime);
vo.setAge(user.getAge());
vo.setGender(user.getGender());
vo.setClinicalDia(consultationRecord.getWesternDiagnosis());
if (Objects.nonNull(clinicWorkLog)){
vo.setMedicalTreatment(clinicWorkLog.getMedicalTreatment());
}
return vo;
}
@PostMapping("/getOwnPrescriptionList")
public Page<UserConsultationRecord> getOwnPrescriptionList(HttpServletRequest request,@RequestBody UniPage<UserConsultationRecord> uniPage){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)){
throw new ServiceException(401,"登录已过期");
}
Page<UserConsultationRecord> page = (Page<UserConsultationRecord>) uniPage.getPage();
LambdaQueryWrapper<UserConsultationRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(UserConsultationRecord::getUserId,currentUser.getId());
wrapper.orderByDesc(UserConsultationRecord::getCreateTime);
return consultationRecordMapper.selectPage(page,wrapper);
}
/*@GetMapping("/getOwnPrescription")
public UserConsultationRecordVO getOwnPrescription(@RequestParam("id") Integer id){
}*/
private String getDrugPrice(){
while (true){
String s = RandomStringUtils.randomNumeric(3);
if (!s.startsWith("0")){
return s;
}
}
}
}
@@ -0,0 +1,210 @@
package cn.unizk.controller;
import cn.unizk.constant.UniConstant;
import cn.unizk.entity.po.Chapter;
import cn.unizk.entity.po.MobileAppUser;
import cn.unizk.entity.po.MobileUserHaveCourse;
import cn.unizk.entity.po.MobileUserVideoProgress;
import cn.unizk.entity.vo.*;
import cn.unizk.exception.ServiceException;
import cn.unizk.mapper.*;
import cn.unizk.utils.BeanCopyUtils;
import cn.unizk.utils.JkSecurityUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* @Author hao
* @Date 2026/3/9 17:43
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/course")
public class CourseStudyController {
@Resource
private CourseMapper courseMapper;
@Resource
private MobileUserHaveCourseMapper mobileUserHaveCourseMapper;
@Resource
private MobileUserVideoProgressMapper mobileUserVideoProgressMapper;
@Resource
private ChapterMapper chapterMapper;
@Resource(name = "kryoRedisTemplate")
private RedisTemplate redisTemplate;
@GetMapping("/coursePage")
public Page<CourseVO> courseVOPage(@RequestParam(value = "currentPage", required = false, defaultValue = "1") int currentPage,
@RequestParam(value = "pageSize", required = false, defaultValue = "10") int pageSize,
@RequestParam(value = "title", required = false) String title, HttpServletRequest request) {
/*MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)) {
throw new ServiceException(401, "登录已过期,请重新登录");
}*/
// List<MobileUserHaveCourse> userHaveCourses = mobileUserHaveCourseMapper.selectList(new LambdaQueryWrapper<MobileUserHaveCourse>().eq(MobileUserHaveCourse::getMobileUserId, currentUser.getId()));
Page<CourseVO> page = new Page<>(currentPage, pageSize);
List<CourseVO> courseVOS = courseMapper.voPage(page, title);
courseVOS.forEach(vo -> vo.setUnlock(1));
/* if (CollectionUtils.isEmpty(userHaveCourses)) {
courseVOS.forEach(vo -> {
vo.setUnlock(0);
});
} else {
List<Integer> courseIds = userHaveCourses.stream().map(MobileUserHaveCourse::getCourseId).collect(Collectors.toList());
for (CourseVO courseVO : courseVOS) {
if (courseIds.contains(courseVO.getCourseId())) {
courseVO.setUnlock(1);
List<Chapter> chapters = chapterMapper.selectList(new LambdaQueryWrapper<Chapter>().eq(Chapter::getCourseId, courseVO.getCourseId()));
if (CollectionUtils.isEmpty(chapters)){
continue;
}
List<Integer> collect = chapters.stream().map(Chapter::getChapterId).collect(Collectors.toList());
List<MobileUserVideoProgress> wxUserVideoProgresses = mobileUserVideoProgressMapper.selectList(new LambdaQueryWrapper<MobileUserVideoProgress>().eq(MobileUserVideoProgress::getMobileUserId, currentUser.getId())
.in(MobileUserVideoProgress::getChapterId, collect));
if (CollectionUtils.isEmpty(wxUserVideoProgresses)) {
courseVO.setProgress(0);
} else {
List<Integer> progresses = wxUserVideoProgresses.stream().map(MobileUserVideoProgress::getProgress).collect(Collectors.toList());
int sum = 0;
for (Integer progress : progresses) {
sum += progress;
}
int i = sum / progresses.size();
courseVO.setProgress(i);
}
} else {
courseVO.setUnlock(0);
}
}
}*/
page.setRecords(courseVOS);
return page;
}
@GetMapping("/chapters")
public Page<ChapterVO> voPage(@RequestParam(value = "currentPage", required = false, defaultValue = "1") int currentPage,
@RequestParam(value = "pageSize", required = false, defaultValue = "50") int pageSize,
@RequestParam("courseId") Integer courseId, HttpServletRequest request) {
/* MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)){
throw new ServiceException(401, "登录已过期,请重新登录");
}
MobileUserHaveCourse userHaveCourse = mobileUserHaveCourseMapper.selectOne(new LambdaQueryWrapper<MobileUserHaveCourse>().eq(MobileUserHaveCourse::getMobileUserId, currentUser.getId())
.eq(MobileUserHaveCourse::getCourseId, courseId));
if (Objects.isNull(userHaveCourse)){
return new Page<>();
}
Page<Chapter> page = new Page<>(currentPage,pageSize);
Page<Chapter> chapterPage = chapterMapper.selectPage(page, new LambdaQueryWrapper<Chapter>().eq(Chapter::getCourseId, courseId));
List<Chapter> chapters = chapterPage.getRecords();
if (CollectionUtils.isEmpty(chapters)){
return new Page<>();
}
Collection<ChapterVO> chapterVOS = BeanCopyUtils.copyArrays(chapters, ChapterVO::new);
for (ChapterVO chapterVO : chapterVOS) {
MobileUserVideoProgress wxUserVideoProgress = mobileUserVideoProgressMapper.selectOne(new LambdaQueryWrapper<MobileUserVideoProgress>()
.eq(MobileUserVideoProgress::getMobileUserId, currentUser.getId())
.eq(MobileUserVideoProgress::getChapterId, chapterVO.getChapterId()));
if (Objects.isNull(wxUserVideoProgress)){
chapterVO.setProgress(0);
}else {
chapterVO.setProgress(wxUserVideoProgress.getProgress());
}
}
Page<ChapterVO> voPage = new Page<>(chapterPage.getCurrent(),chapterPage.getSize());
voPage.setTotal(chapterPage.getTotal());
voPage.setRecords((List<ChapterVO>) chapterVOS);*/
pageSize = 50;
Page<Chapter> page = new Page<>(currentPage,pageSize);
Page<Chapter> chapterPage = chapterMapper.selectPage(page, new LambdaQueryWrapper<Chapter>().eq(Chapter::getCourseId, courseId));
List<Chapter> chapters = chapterPage.getRecords();
if (CollectionUtils.isEmpty(chapters)){
return new Page<>();
}
Collection<ChapterVO> chapterVOS = BeanCopyUtils.copyArrays(chapters, ChapterVO::new);
Page<ChapterVO> voPage = new Page<>(chapterPage.getCurrent(),chapterPage.getSize());
voPage.setTotal(chapterPage.getTotal());
chapterVOS.forEach(chapterVO -> chapterVO.setProgress(80));
voPage.setRecords((List<ChapterVO>) chapterVOS);
return voPage;
}
@PostMapping("/reportWatch")
public ResultData reportWatch(@RequestBody @Validated VideoProgressVO videoProgressVO,HttpServletRequest request){
/*MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)){
throw new ServiceException(401, "登录已过期,请重新登录");
}*/
/*String key = UniConstant.VIDEO_PROGRESS_CACHE + currentUser.getId() + ":" + videoProgressVO.getChapterId();
HashOperations<String, String, Object> ops = redisTemplate.opsForHash();
ops.put(key, "lastPosition", videoProgressVO.getLastPosition());
ops.increment(key,"spentTime",videoProgressVO.getSpentTime());
redisTemplate.expire(key, 10, TimeUnit.MINUTES);
return ResultData.success();*/
/* MobileUserVideoProgress wxUserVideoProgress = mobileUserVideoProgressMapper.selectOne(new LambdaQueryWrapper<MobileUserVideoProgress>().eq(MobileUserVideoProgress::getChapterId, videoProgressVO.getChapterId())
.eq(MobileUserVideoProgress::getMobileUserId, currentUser.getId()));
if (Objects.isNull(wxUserVideoProgress)){
MobileUserVideoProgress videoProgress = new MobileUserVideoProgress();
videoProgress.setChapterId(videoProgressVO.getChapterId());
videoProgress.setMobileUserId(currentUser.getId());
videoProgress.setLastPosition(videoProgressVO.getLastPosition());
videoProgress.setSpentTime(videoProgressVO.getSpentTime());
videoProgress.setUpdateTime(LocalDateTime.now());
Chapter chapter = chapterMapper.selectById(videoProgressVO.getChapterId());
if (Objects.nonNull(chapter)){
Integer p = (videoProgressVO.getLastPosition() * 100) / chapter.getVideoDuration();
videoProgress.setProgress(p);
}else {
videoProgress.setProgress(0);
}
mobileUserVideoProgressMapper.insert(videoProgress);
}else {
wxUserVideoProgress.setSpentTime(videoProgressVO.getSpentTime());
if (videoProgressVO.getLastPosition() > wxUserVideoProgress.getLastPosition()){
wxUserVideoProgress.setLastPosition(videoProgressVO.getLastPosition());
Chapter chapter = chapterMapper.selectById(videoProgressVO.getChapterId());
if (Objects.nonNull(chapter)){
Integer p = (videoProgressVO.getLastPosition() * 100) / chapter.getVideoDuration();
wxUserVideoProgress.setProgress(p);
}else {
wxUserVideoProgress.setProgress(0);
}
}
mobileUserVideoProgressMapper.updateById(wxUserVideoProgress);
}*/
return ResultData.success();
}
@PostMapping("/reportTotalDuration")
public ResultData reportTotalDuration(@RequestParam("totalDuration") Integer totalDuration,@RequestParam("chapterId") Integer chapterId){
Chapter chapter = new Chapter();
chapter.setChapterId(chapterId);
chapter.setVideoDuration(totalDuration);
chapterMapper.updateById(chapter);
return ResultData.success();
}
@PostMapping("/buyCourse")
public void buyCourse(@Validated @RequestBody BuyCourseVO vo){
}
}
@@ -0,0 +1,91 @@
package cn.unizk.controller;
import cn.unizk.entity.vo.QuestionAnswerVO;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.entity.vo.SeverityVO;
import cn.unizk.exception.ServiceException;
import cn.unizk.service.impl.DoubaoServiceImpl;
import lombok.extern.slf4j.Slf4j;
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.RestController;
import javax.annotation.Resource;
import java.util.Objects;
/**
* @Author hao
* @Date 2026/5/12 15:37
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/doubao")
@Slf4j
public class DoubaoAIController {
@Resource
private DoubaoServiceImpl doubaoService;
@PostMapping("/getAnswer")
public ResultData getAnswer(@RequestBody QuestionAnswerVO vo){
log.info("进入getAnswer接口,AI判断回答---------------");
String input1 = String.format("问题:%s?回答:%s",vo.getQuestion(),vo.getAnswer());
String input2 = "请判断:回答和问题是否相干?只需要回答【相干】或【不相干】,不要多余文字。";
String result = doubaoService.drawConclusion(input1, input2);
ResultData data = new ResultData();
if ("【相干】".equals(result)){
data.setCode(200);
}else {
data.setCode(300);
}
return data;
}
@PostMapping("/severity")
public ResultData<String> severity(@RequestBody SeverityVO vo){
if (Objects.isNull(vo)){
throw new ServiceException("requestBody cannot empty");
}
if (vo.getId() != 6 && vo.getId() != 8){
throw new ServiceException("无效ID");
}
ResultData<String> data = new ResultData<>();
data.setCode(200);
if (vo.getId() == 6){
String input2 = "请判断:回答内容包含的时间是否小于或等于一个月?只需要回答【yes】或【no】,不要多余文字";
String input1 = String.format("回答内容:%s",vo.getAnswer());
String s = doubaoService.drawConclusion(input1, input2);
log.info("ai回复:",s);
if ("【yes】".contains(s)){
data.setData("000");
}else {
data.setData("111");
}
return data;
}
if (vo.getId() == 8){
String input2 = "假设回复内容是患者回复疼痛程度情况的,请判断:回复内容的疼痛程度是否是轻度或者比轻度更轻?只需要回答【yes】或【no】,不要多余文字";
String input1 = String.format("回复内容:%s",vo.getAnswer());
String s = doubaoService.drawConclusion(input1, input2);
log.info("豆包返回:",s);
if ("【yes】".contains(s)){
data.setData("000");
}else {
data.setData("111");
}
return data;
}
throw new ServiceException("系统异常");
}
@PostMapping("/getReport")
public ResultData<String> getReport(@RequestBody String text){
String input2 = "请你将上述这句话用尽可能简洁的文字总结出来";
String conclusion = doubaoService.drawConclusion(text, input2);
ResultData<String> data = new ResultData<>();
data.setCode(200);
data.setData(conclusion);
return data;
}
}
@@ -0,0 +1,51 @@
package cn.unizk.controller;
import cn.unizk.entity.po.SysFile;
import cn.unizk.entity.vo.MinioRespVO;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.enums.FileType;
import cn.unizk.exception.ServiceException;
import cn.unizk.service.FileService;
import cn.unizk.service.FileUploadService;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.util.Set;
/**
* @Author hao
* @Date 2025/10/9 17:19
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/file")
public class FileController {
@Resource
private FileService fileService;
@Resource
private FileUploadService fileUploadService;
@GetMapping("/getById/{id}")
public SysFile getById(@PathVariable("id") Integer id){
return fileService.getById(id);
}
@PostMapping("/uploadFile")
public ResultData<MinioRespVO> uploadFile(@RequestParam("file") MultipartFile file){
String originalFilename = file.getOriginalFilename();
if (StringUtils.isBlank(originalFilename) || originalFilename.lastIndexOf(".") == -1){
throw new ServiceException("文件格式不正确");
}
String fileType = originalFilename.substring(originalFilename.lastIndexOf("."));
Set<String> types = ImmutableSet.of(".png",".jpg",".jpeg",".ico");
if (!types.contains(fileType)){
throw new ServiceException("目前只支持上传png,jpg,jpeg,ico类型文件");
}
String bucketName = FileType.getMinioBucketName(file.getContentType()).getBucketName();
return fileUploadService.simpleUpload(file, bucketName);
}
}
@@ -0,0 +1,136 @@
package cn.unizk.controller;
import cn.unizk.entity.po.Appointments;
import cn.unizk.entity.po.GolfCustInfo;
import cn.unizk.entity.po.MobileAppUser;
import cn.unizk.entity.po.MobileAppointments;
import cn.unizk.entity.vo.GrfReportVO;
import cn.unizk.entity.vo.GrfdialogueVO;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.mapper.*;
import cn.unizk.service.impl.DoubaoServiceImpl;
import cn.unizk.utils.JkSecurityUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @Author hao
* @Date 2026/6/5 11:26
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/golf")
@Slf4j
public class GolfController {
@Resource
private DoubaoServiceImpl doubaoService;
@Resource
private GolfCustInfoMapper golfCustInfoMapper;
@Resource
private MobileAppUserMapper mobileAppUserMapper;
@Resource
private MobileAppointmentsMapper mobileAppointmentsMapper;
@PostMapping("/judgeOne")
public ResultData judgeOne(@Validated @RequestBody GrfdialogueVO vo){
ResultData data = new ResultData();
if (StringUtils.isBlank(vo.getQuestion())){
String input1 = "请判断下面的回答是否是肩部,腰部,肘部,手腕,膝关节,髋关节,颈部,踝关节有关这8个部位的内容,只需要回答是或不是。";
String s = doubaoService.drawConclusion(input1, vo.getAnswer());
log.info(s);
System.out.println(String.format("【%s】",s));
if ("是".equals(s)){
data.setCode(200);
return data;
}
data.setCode(300);
return data;
}else {
String question = vo.getQuestion();
switch (question){
case "2":
vo.setQuestion("请问您肌肉发紧僵硬吗?");
break;
case "3":
vo.setQuestion("请问您关节活动怎么样?");
break;
case "4":
vo.setQuestion("请问您挥杆有没有不适症状?");
break;
}
String input1 = String.format("问题:%s;回答:%s",vo.getQuestion(),vo.getAnswer());
String input2 = "请判断:回答和问题是否相干?只需要回答相干或不相干,不要多余文字。";
String s = doubaoService.drawConclusion(input1, input2);
if ("相干".equals(s)){
data.setCode(200);
}else {
data.setCode(300);
}
return data;
}
}
@PostMapping("/saveCustInfo")
public ResultData saveCustInfo(@Validated @RequestBody GolfCustInfo custInfo){
golfCustInfoMapper.insert(custInfo);
return ResultData.success(custInfo.getId());
}
@PostMapping("/getReport")
public ResultData<GrfReportVO> getReport(@RequestParam("id") Integer id,
HttpServletRequest request,
@RequestBody GrfReportVO vo){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
GolfCustInfo golfCustInfo = golfCustInfoMapper.selectById(id);
String sex = golfCustInfo.getGender() == 0 ? "女" : "男";
StringBuilder sb = new StringBuilder();
sb.append("就诊人:").append(golfCustInfo.getCustName())
.append("性别:").append(sex).append("年龄:").append(golfCustInfo.getAge())
.append("高尔夫球龄:").append(golfCustInfo.getGolfingAge())
.append("每周打球次数:").append(golfCustInfo.getWeekFrequency())
.append("患者陈述:").append(vo.getAnswer1()).append(",").append(vo.getAnswer2())
.append(",").append(vo.getAnswer3()).append(",").append(vo.getAnswer4());
String conclusion = doubaoService.drawConclusion("请你把下面内容总结成一段话,并且这段话要包括病情分析,比如是得了肩周炎,腰椎间盘突出还是其他什么病,并给出调理建议", sb.toString());
vo.setReport(conclusion);
vo.setName(golfCustInfo.getCustName());
vo.setGender(golfCustInfo.getGender()==0 ? "女" : "男");
vo.setAge(golfCustInfo.getAge());
vo.setPhoneNumber(golfCustInfo.getPhoneNumber());
vo.setGolfingAge(golfCustInfo.getGolfingAge());
vo.setWeekFrequency(golfCustInfo.getWeekFrequency());
new Thread(() -> {
List<Integer> numbers = new ArrayList<>();
for (int i = 1; i <= 13; i++) {
numbers.add(i);
}
Collections.shuffle(numbers);
MobileAppUser user = new MobileAppUser();
user.setId((currentUser).getId());
user.setPainNumber(String.valueOf(numbers.get(0)) + "," + numbers.get(1));
mobileAppUserMapper.updateById(user);
}).start();
return ResultData.success(vo);
}
@PostMapping("/evaluation")
public ResultData evaluation(@RequestBody MobileAppointments appointments){
MobileAppointments app = new MobileAppointments();
app.setId(appointments.getId());
app.setTreatRating(appointments.getTreatRating());
app.setEvaluation(appointments.getEvaluation());
app.setStatus("commented");
mobileAppointmentsMapper.updateById(app);
return ResultData.success();
}
}
@@ -0,0 +1,198 @@
package cn.unizk.controller;
import cn.unizk.constant.UniConstant;
import cn.unizk.entity.po.*;
import cn.unizk.entity.vo.ComboVO;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.enums.OrderStatus;
import cn.unizk.exception.ServiceException;
import cn.unizk.mapper.MobileAppUserMapper;
import cn.unizk.mapper.MobileOrderMapper;
import cn.unizk.mapper.PhysioSchedulesMapper;
import cn.unizk.mapper.UserDeviceTreatRecordMapper;
import cn.unizk.service.ComboService;
import cn.unizk.service.DeviceService;
import cn.unizk.service.WxUserService;
import cn.unizk.utils.AESUtil;
import cn.unizk.utils.SendSmsUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* @Author hao
* @Date 2025/9/11 12:00
* @Version 1.0
**/
@Slf4j
@RestController
@RequestMapping("/h5")
@CrossOrigin(origins = {"http://369.peaktcm.com:9000","http://peaktcm.com","http://peaktcm.com:9000","http://139.9.2.195:8080"},allowCredentials = "true",
exposedHeaders = {"Authorization"})
public class H5ComboController {
@Resource
private MobileOrderMapper mobileOrderMapper;
@Resource
private ComboService comboService;
@Resource
private MobileAppUserMapper mobileAppUserMapper;
@Autowired
@Qualifier("kryoRedisTemplate")
private RedisTemplate<String,Object> redisTemplate;
@Resource
private UserDeviceTreatRecordMapper treatRecordMapper;
@Resource
private DeviceService deviceService;
@Resource
private PhysioSchedulesMapper physioSchedulesMapper;
@GetMapping("/getCombos")
public List<ComboVO> getCombos(HttpServletResponse response, @RequestParam("deviceId") String deviceId, @RequestParam("phoneNumber") String phoneNumber,
@RequestParam("code") String code){
log.info("deviceId={},phoneNumber={},code={}",deviceId,phoneNumber,code);
validatePhoneCode(phoneNumber,code);
MobileAppUser one = mobileAppUserMapper.selectOne(new LambdaQueryWrapper<MobileAppUser>().eq(MobileAppUser::getPhoneNumber, phoneNumber));
if (Objects.isNull(one)){
throw new ServiceException("您不是吉客中医微信小程序的用户");
}
List<MobileOrder> wxOrders = mobileOrderMapper.selectList(new LambdaQueryWrapper<MobileOrder>()
.eq(MobileOrder::getMobileUserId, one.getId())
.eq(MobileOrder::getDeviceId, deviceId)
.in(MobileOrder::getStatus,OrderStatus.paid.name(),OrderStatus.unverified.name())
.isNotNull(MobileOrder::getComboId));
if (CollectionUtils.isEmpty(wxOrders)){
throw new ServiceException("抱歉,您在此设备上没有购买套餐");
}
String encrypt = phoneNumber;
try {
encrypt = AESUtil.encrypt(phoneNumber);
} catch (Exception e) {
log.error("手机号加密异常",e);
}
response.setHeader("Authorization",encrypt);
response.setHeader("Access-Control-Expose-Headers", "Authorization");
List<ComboVO> list = new ArrayList<>();
for (MobileOrder wxOrder : wxOrders) {
Combo combo = comboService.getById(wxOrder.getComboId());
ComboVO vo = new ComboVO();
BeanUtils.copyProperties(combo,vo);
vo.setOrderId(wxOrder.getOrderId());
list.add(vo);
}
return list;
}
@GetMapping("/sendPhoneCode")
public ResultData sendPhoneCode(@RequestParam("phoneNumber") String phoneNumber){
String code = RandomStringUtils.randomNumeric(6);
/*redisTemplate.opsForValue().set("h5:"+phoneNumber,"888888");
return ResultData.success();*/
try {
String s = SendSmsUtil.sendPhoneCode(phoneNumber, code);
JSONObject res = JSONObject.parseObject(s);
Integer statusCode = res.getInteger("statusCode");
if (statusCode != null && statusCode == 200){
redisTemplate.opsForValue().set("h5:"+phoneNumber,code,300, TimeUnit.SECONDS);
return ResultData.success();
}
return ResultData.failure(500,"验证码发送失败");
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
throw new ServiceException("验证码发送失败");
}
}
@GetMapping("/writeOff")
public int writeOff(HttpServletRequest request,@RequestParam("orderId") String orderId){
String encrypt = request.getHeader("Token");
if (StringUtils.isBlank(encrypt)){
throw new ServiceException("No permission");
}
String decrypt = encrypt;
try {
decrypt = AESUtil.decrypt(encrypt);
} catch (Exception e) {
log.error("手机号解密异常",e);
}
// MobileOrder wxOrder = orderService.getOrderByIdAndPhone(orderId, decrypt);
MobileOrder mobileOrder = mobileOrderMapper.selectOrderByIdAndPhone(orderId, decrypt);
if (Objects.isNull(mobileOrder)){
throw new ServiceException("No permission");
}
if (!OrderStatus.unverified.name().equals(mobileOrder.getStatus())){
throw new ServiceException("该套餐已核销过了!");
}
Combo combo = comboService.getById(mobileOrder.getComboId());
if (Objects.isNull(combo) || StringUtils.isBlank(combo.getDeviceId())){
throw new ServiceException("套餐或设备不存在,无法核销!");
}
Device dv = deviceService.getById(combo.getDeviceId());
if (dv.getTreatStatus() == 1 || dv.getOperatingStatus() == 0){
throw new ServiceException("此设备正在被使用,等待使用完再进行核销!");
}
UserDeviceTreatRecord treatRecord = new UserDeviceTreatRecord();
treatRecord.setDeviceId(combo.getDeviceId());
treatRecord.setUserId(mobileOrder.getMobileUserId());
treatRecord.setCreateTime(LocalDateTime.now());
treatRecordMapper.insert(treatRecord);
Device device = new Device();
device.setDeviceId(combo.getDeviceId());
device.setTreatStatus(1);
deviceService.updateById(device);
mobileOrder.setStatus(OrderStatus.verified.name());
String redisKey = UniConstant.REDIS_CURRENT_DEVICE_USER + dv.getDeviceId();
redisTemplate.opsForValue().set(redisKey,mobileOrder.getMobileUserId(),10,TimeUnit.MINUTES);
return mobileOrderMapper.updateById(mobileOrder);
}
@GetMapping("/getAuthQrcode")
public void getAuthQrcode(HttpServletResponse response){
// QRcodeUtil.generateQrcode(response);
}
private void validatePhoneCode(String phone,String code){
String cacheCode = (String) redisTemplate.opsForValue().get("h5:"+phone);
if (StringUtils.isBlank(cacheCode) || !cacheCode.equals(code)){
throw new ServiceException("验证码错误");
}
redisTemplate.delete("h5:"+phone);
}
@GetMapping("/addData")
public Integer addData(){
List<String> slotCodes = Lists.newArrayList("M1","M2","M3","A1","A2","A3","A4");
List<PhysioSchedules> list = new ArrayList<>();
for (String slotCode : slotCodes) {
PhysioSchedules physioSchedules = new PhysioSchedules();
physioSchedules.setPhysioId(8);
physioSchedules.setTimeSlotCode(slotCode);
physioSchedules.setMaxBookings(1);
physioSchedules.setActive(1);
list.add(physioSchedules);
}
physioSchedulesMapper.insert(list);
return 1;
}
}
@@ -0,0 +1,36 @@
package cn.unizk.controller;
import cn.unizk.entity.po.Device;
import cn.unizk.huifupay.config.properties.HuiFuProperties;
import cn.unizk.service.DeviceService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.annotation.Resource;
import java.util.Objects;
/**
* @Author hao
* @Date 2026/3/10 17:39
* @Version 1.0
**/
@Controller
@RequestMapping("/h5/scan")
public class H5PowerOnController {
@Resource
private DeviceService deviceService;
@Resource
private HuiFuProperties huiFuProperties;
@GetMapping("/toPayPage")
public String toPayPage(@RequestParam("deviceId") String deviceId){
Device device = deviceService.getById(deviceId);
if (Objects.isNull(device) || device.getForcePay() != 1){
return "redirect:/h5/hello.html?deviceId=" + deviceId;
}
return "redirect:/h5/pay.html?deviceId=" + deviceId;
}
}
@@ -0,0 +1,210 @@
package cn.unizk.controller;
import cn.hutool.core.date.DateUtil;
import cn.unizk.config.WxProperties;
import cn.unizk.entity.po.MobileOrder;
import cn.unizk.entity.vo.MobileOrderVO;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.enums.OrderStatus;
import cn.unizk.huifupay.config.properties.HuiFuProperties;
import cn.unizk.huifupay.utils.HfUtil;
import cn.unizk.huifupay.utils.HuifuSignUtil;
import cn.unizk.mapper.MobileOrderMapper;
import cn.unizk.service.PayService;
import cn.unizk.utils.HttpUtil;
import cn.unizk.wxPay.vo.WxPlaceOrderResVO;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.*;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.LocalDateTime;
import java.util.*;
/**
* @Author hao
* @Date 2025/9/29 11:31
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/huifuPay")
@Slf4j
public class HuiFuPayController {
@Resource
private HuiFuProperties huiFuProperties;
@Resource
private WxProperties wxProperties;
@Resource(name = "huifu")
private PayService payService;
@Resource
private MobileOrderMapper mobileOrderMapper;
@PostMapping("/placeOrder")
public WxPlaceOrderResVO placeOrder(@RequestBody MobileOrderVO vo){
return payService.wxPlaceOrder(vo);
}
@PostMapping("/configureApplet")
public String configureApplet(){
return payService.configureApplet();
}
@PostMapping("/realNameCheck")
public ResultData realNameCheck(){
JSONObject object = JSONObject.of();
object.put("sys_id",huiFuProperties.getHuifuId());
object.put("product_id",huiFuProperties.getProductId());
JSONObject data = JSONObject.of();
data.put("req_seq_id",HfUtil.getReqSeqId());
data.put("req_date",DateUtil.format(new Date(),"yyyyMMdd"));
data.put("huifu_id",huiFuProperties.getHuifuId());
data.put("pay_scene","02");
data.put("owner","Y");
data.put("identification_address","山东省青岛市市北区包头路24号甲");
JSONArray jsonArray = new JSONArray();
JSONObject ubo_info_list = JSONObject.of();
ubo_info_list.put("ubo_id_doc_type","00");
ubo_info_list.put("ubo_id_doc_copy","d36573e8-beda-34d8-9647-ee75d02e6f6b");
ubo_info_list.put("ubo_id_doc_copy_back","c7ed0c5e-bb3a-31b6-ac1d-37c02c702da9");
ubo_info_list.put("ubo_id_doc_name","陈静");
ubo_info_list.put("ubo_id_doc_number","370203197102050021");
ubo_info_list.put("ubo_id_doc_address","山东省青岛市市北区包头路24号甲");
ubo_info_list.put("ubo_period_begin","20150902");
ubo_info_list.put("ubo_period_end","20350902");
jsonArray.add(ubo_info_list);
data.put("ubo_info_list",jsonArray.toString());
data.put("name","陈静");
data.put("mobile","13686435231");
data.put("id_card_number","370203197102050021");
data.put("contact_type","LEGAL");
object.put("data",data);
String sign = HuifuSignUtil.sign(data, huiFuProperties.getPrivateKey());
object.put("sign",sign);
String url = "https://api.huifu.com/v2/merchant/busi/realname";
log.info("参数:{}",object);
String s = HttpUtil.doPostJson(url, null, object);
log.info("汇付实名认证返回:{}",s);
return ResultData.success(JSONObject.parseObject(s));
}
@PostMapping("/uploadHfData")
public String uploadHfData(@RequestParam("file")MultipartFile file,@RequestParam("fileType") String fileType){
//图片上传接口地址
String url = "https://api.huifu.com/v2/supplementary/picture";
// 请求参数
Map<String, Object> params = new HashMap<String, Object>();
// 请求流水号,需保证当天商户下唯一,推荐采用日期时间+几位流水号的形式
params.put("req_seq_id", HfUtil.getReqSeqId());
params.put("req_date", DateUtil.format(new Date(),"yyyyMMdd"));
params.put("file_type", fileType);
//如果商户还未成功进件huifu_id可以为空
params.put("huifu_id", huiFuProperties.getHuifuId());
String data = JSON.toJSONString(params);
log.info("data={}",data);
CloseableHttpClient httpclient1 = HttpClients.createDefault();
CloseableHttpResponse response = null;
String result = null;
try {
File tempFile = File.createTempFile("upload", ".jpg");
file.transferTo(tempFile);
HttpPost httpPost = new HttpPost(url);
ContentType contentType = ContentType.create("text/plain", Charset.forName("UTF-8"));
MultipartEntityBuilder mEntityBuilder = MultipartEntityBuilder.create();
mEntityBuilder.addTextBody("sys_id", huiFuProperties.getHuifuId(), contentType);
mEntityBuilder.addTextBody("product_id", huiFuProperties.getProductId(), contentType);
mEntityBuilder.addTextBody("data", data, contentType);
mEntityBuilder.addBinaryBody("file", tempFile);
httpPost.setEntity(mEntityBuilder.build());
response = httpclient1.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
HttpEntity resEntity = response.getEntity();
result = EntityUtils.toString(resEntity);
System.out.print(result);
tempFile.deleteOnExit();
return result;
}
} catch (IOException e) {
e.printStackTrace();
}
return "fail";
}
@PostMapping("/callback")
public String callback(@RequestBody String messageBody){
log.info("汇付支付成功回调数据:{}",messageBody);
try {
String decode = URLDecoder.decode(messageBody, "utf-8");
String resp_data = decode.split("&resp_data=")[1];
JSONObject jsonObject = JSONObject.parseObject(resp_data);
String trans_stat = jsonObject.getString("trans_stat");
String req_seq_id = jsonObject.getString("req_seq_id");
List<MobileOrder> wxOrders = mobileOrderMapper.selectList(new LambdaQueryWrapper<MobileOrder>().eq(MobileOrder::getOutTradeNo, req_seq_id));
if (CollectionUtils.isEmpty(wxOrders)){
log.info("查无此单,交易流水号:{}",req_seq_id);
return "fail";
}
if ("S".equals(trans_stat)){
log.info("交易成功");
//修改订单状态
for (MobileOrder wxOrder : wxOrders) {
if (wxOrder.getComboId() != null){
wxOrder.setDeliveryWay("自提");
}
if (wxOrder.getComboId() != null || wxOrder.getDeliveryWay().equals("自提")){
wxOrder.setStatus(OrderStatus.unverified.name());
}else if (wxOrder.getComboId() == null && wxOrder.getDeliveryWay().equals("配送")){
wxOrder.setStatus(OrderStatus.not_shipped.name());
}else if(wxOrder.getDeliveryWay().equals("配送")){
wxOrder.setStatus(OrderStatus.not_shipped.name());
}else if(StringUtils.isNotBlank(wxOrder.getReceivingAddress())){
wxOrder.setStatus(OrderStatus.not_shipped.name());
}else {
wxOrder.setStatus(OrderStatus.paid.name());
}
wxOrder.setPayTime(LocalDateTime.now(Clock.systemDefaultZone()));
mobileOrderMapper.updateById(wxOrder);
}
return "success";
}else if ("F".equals(trans_stat)){
log.info("交易失败");
return "fail";
}
} catch (UnsupportedEncodingException e) {
log.error("支付回调解码失败",e);
return "fail";
}
return "fail";
}
@RequestMapping("/configureAppletCallback")
public String configureAppletCallback(@RequestBody String messageBody){
log.info("汇付配置微信商户小程序审核消息:{}",messageBody);
return "success";
}
}
@@ -0,0 +1,79 @@
package cn.unizk.controller;
import cn.unizk.entity.dto.CashOutDTO;
import cn.unizk.entity.dto.CashOutResponse;
import cn.unizk.entity.po.MobileAppUser;
import cn.unizk.entity.vo.PayCourseVO;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.exception.ServiceException;
import cn.unizk.huifupay.dto.HuifuPlaceOrderParamBaseDTO;
import cn.unizk.huifupay.service.HuiFuVendorPayService;
import cn.unizk.huifupay.vo.DeviceScanPayVO;
import cn.unizk.huifupay.vo.RechargeVO;
import cn.unizk.utils.JkSecurityUtil;
import cn.unizk.wxPay.vo.WxPlaceOrderResVO;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
/**
* @Author hao
* @Date 2026/3/11 14:14
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/huiFuVendor")
public class HuiFuVendorPayController {
@Resource
private HuiFuVendorPayService vendorPayService;
@PostMapping("/pay")
public WxPlaceOrderResVO pay(@RequestBody DeviceScanPayVO vo, HttpServletRequest request){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser) || !currentUser.getId().equals(vo.getUserId())){
throw new ServiceException(401,"非法访问!");
}
vo.setUserId(currentUser.getId());
return vendorPayService.vendorPay(vo);
}
@PostMapping("/cashOut")
public CashOutResponse cashOut(@RequestBody CashOutDTO dto){
return vendorPayService.profitSharingPay(dto);
}
@PostMapping("/coursePay")
public WxPlaceOrderResVO coursePay(@RequestBody PayCourseVO vo,HttpServletRequest request){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser) || !currentUser.getId().equals(vo.getUserId())){
throw new ServiceException(401,"非法访问!");
}
vo.setUserId(currentUser.getId());
return vendorPayService.coursePay(vo);
}
@PostMapping("/vipRecharge")
public WxPlaceOrderResVO vipRecharge(@RequestBody RechargeVO vo,HttpServletRequest request){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (currentUser == null || !currentUser.getId().equals(vo.getUserId())){
throw new ServiceException(401,"非法访问!");
}
vo.setUserId(currentUser.getId());
return vendorPayService.recharge(vo);
}
@PostMapping("/activeMembership")
public WxPlaceOrderResVO activeMembership(@RequestBody HuifuPlaceOrderParamBaseDTO dto,HttpServletRequest request){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (currentUser == null || !currentUser.getId().equals(dto.getUserId())){
throw new ServiceException(401,"非法访问!");
}
dto.setUserId(currentUser.getId());
return vendorPayService.activeMembership(dto);
}
}
@@ -0,0 +1,307 @@
package cn.unizk.controller;
import cn.unizk.constant.UniConstant;
import cn.unizk.entity.dto.HuifuSyncRefundResDTO;
import cn.unizk.entity.po.*;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.enums.OrderAfterSaleStatus;
import cn.unizk.enums.OrderStatus;
import cn.unizk.exception.ServiceException;
import cn.unizk.mapper.*;
import cn.unizk.service.WxUserService;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
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.RestController;
import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLDecoder;
import java.time.Clock;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* @Author hao
* @Date 2025/10/23 15:56
* @Version 1.0
**/
@RestController
@RequestMapping("/hf/webhook")
@Slf4j
public class HuifuWebhookController {
@Resource
private MobileOrderMapper mobileOrderMapper;
@Resource
private DeviceMapper deviceMapper;
@Resource
private StoreAccountMapper storeAccountMapper;
@Resource
private JkStoreMapper jkStoreMapper;
@Resource
private MobileAppUserMapper mobileAppUserMapper;
@Resource
private MobileUserHaveCourseMapper mobileUserHaveCourseMapper;
@Resource
private ChildStoreAccountMapper childStoreAccountMapper;
@Resource(name = "kryoRedisTemplate")
private RedisTemplate redisTemplate;
@Resource
private WxVipRechargeMapper vipRechargeMapper;
@Resource
private MobileAppUserAccountMapper mobileAppUserAccountMapper;
@PostMapping("/refundCallback")
public String refundCallback(@RequestBody String body) throws UnsupportedEncodingException {
log.info("退款回调接口收到请求参数:{}",body);
String decode = URLDecoder.decode(body, "utf-8");
String resp_data = decode.split("&resp_data=")[1];
JSONObject jsonObject = JSONObject.parseObject(resp_data);
String sub_resp_code = jsonObject.getString("sub_resp_code");
if ("00000000".equals(sub_resp_code)){
String req_seq_id = jsonObject.getString("req_seq_id");
List<MobileOrder> wxOrders = mobileOrderMapper.selectList(new LambdaQueryWrapper<MobileOrder>().eq(MobileOrder::getRefundReqSeqId, req_seq_id));
if (CollectionUtils.isNotEmpty(wxOrders)){
log.info("此订单是微信小程序订单,交易流水号:{}",req_seq_id);
String trans_stat = jsonObject.getString("trans_stat");
if ("S".equals(trans_stat)){
for (MobileOrder wxOrder : wxOrders) {
wxOrder.setStatus(OrderStatus.refunded.name());
wxOrder.setRefundFinishTime(LocalDateTime.now());
if (wxOrder.getAfterSaleStatus().equals(OrderAfterSaleStatus.ONLY_TUI_KUAN.getKey())){
wxOrder.setAfterSaleStatus(OrderAfterSaleStatus.TUI_KUAN_SUCCESS.getKey());
}
if (wxOrder.getAfterSaleStatus().equals(OrderAfterSaleStatus.TUI_HUO_TUI_KUAN.getKey())){
wxOrder.setAfterSaleStatus(OrderAfterSaleStatus.TUI_HUO_TUI_KUAN_SUCCESS.getKey());
}
}
mobileOrderMapper.updateById(wxOrders);
return "ok";
}else if ("F".equals(trans_stat)){
log.info("退款失败,原因:{}",jsonObject.getString("sub_resp_desc"));
}else if ("P".equals(trans_stat)){
log.info("交易中交易中交易中交易中交易中交易中交易中交易中交易中");
}
}else {
List<MobileOrder> mobileOrders = mobileOrderMapper.selectList(new LambdaQueryWrapper<MobileOrder>().eq(MobileOrder::getRefundReqSeqId, req_seq_id));
if (CollectionUtils.isNotEmpty(mobileOrders)){
log.info("此订单是微信小程序订单,交易流水号:{}",req_seq_id);
String trans_stat = jsonObject.getString("trans_stat");
if ("S".equals(trans_stat)){
for (MobileOrder mobileOrder : mobileOrders) {
mobileOrder.setStatus(OrderStatus.refunded.name());
mobileOrder.setRefundFinishTime(LocalDateTime.now());
if (mobileOrder.getAfterSaleStatus().equals(OrderAfterSaleStatus.ONLY_TUI_KUAN.getKey())){
mobileOrder.setAfterSaleStatus(OrderAfterSaleStatus.TUI_KUAN_SUCCESS.getKey());
}
if (mobileOrder.getAfterSaleStatus().equals(OrderAfterSaleStatus.TUI_HUO_TUI_KUAN.getKey())){
mobileOrder.setAfterSaleStatus(OrderAfterSaleStatus.TUI_HUO_TUI_KUAN_SUCCESS.getKey());
}
}
mobileOrderMapper.updateById(mobileOrders);
return "ok";
}else if ("F".equals(trans_stat)){
log.info("退款失败,原因:{}",jsonObject.getString("sub_resp_desc"));
}else if ("P".equals(trans_stat)){
log.info("交易中交易中交易中交易中交易中交易中交易中交易中交易中");
}
}else {
log.info("微信小程序和APP端都查不到该订单,交易流水号:{}",req_seq_id);
return "fail";
}
}
}else {
log.info("退款失败,原因:{}",jsonObject.getString("sub_resp_desc"));
}
return "fail";
}
@PostMapping("/vendorPaySuccess")
@Transactional(rollbackFor = Exception.class)
public String callback(@RequestBody String messageBody){
log.info("汇付商户支付成功回调数据:{}",messageBody);
try {
String decode = URLDecoder.decode(messageBody, "utf-8");
String resp_data = decode.split("&resp_data=")[1];
JSONObject jsonObject = JSONObject.parseObject(resp_data);
String trans_stat = jsonObject.getString("trans_stat");
String req_seq_id = jsonObject.getString("req_seq_id");
List<MobileOrder> wxOrders = mobileOrderMapper.selectList(new LambdaQueryWrapper<MobileOrder>().eq(MobileOrder::getOutTradeNo, req_seq_id));
if (CollectionUtils.isEmpty(wxOrders)){
log.info("查无此单,交易流水号:{}",req_seq_id);
return "fail";
}
MobileOrder wxOrder = wxOrders.get(0);
if (OrderStatus.paid.name().equals(wxOrder.getStatus())){
return "success";
}
if ("S".equals(trans_stat)){
log.info("交易成功");
//修改订单状态
if (wxOrder.getOrderType() != null && wxOrder.getOrderType() == 1){
scanPay(wxOrder);
}else if (wxOrder.getOrderType() != null && wxOrder.getOrderType() == 3){
coursePay(wxOrder);
}else if (wxOrder.getOrderType() != null && wxOrder.getOrderType() == 4){
vipRecharge(wxOrder);
}else if (wxOrder.getOrderType() != null && wxOrder.getOrderType() == 6){
activeMember(wxOrder);
}
return "success";
}else if ("F".equals(trans_stat)){
log.info("交易失败");
return "fail";
}
} catch (UnsupportedEncodingException e) {
log.error("支付回调解码失败",e);
return "fail";
}
return "fail";
}
private void activeMember(MobileOrder wxOrder){
wxOrder.setStatus(OrderStatus.paid.name());
wxOrder.setPayTime(LocalDateTime.now());
mobileOrderMapper.updateById(wxOrder);
MobileAppUser wxUser = mobileAppUserMapper.selectOne(new LambdaQueryWrapper<MobileAppUser>().eq(MobileAppUser::getId, wxOrder.getMobileUserId()));
wxUser.setHasMember(1);
mobileAppUserMapper.updateById(wxUser);
}
private void scanPay(MobileOrder wxOrder){
wxOrder.setStatus(OrderStatus.paid.name());
Device device = deviceMapper.selectById(wxOrder.getDeviceId());
wxOrder.setStoreId(device.getStoreId());
wxOrder.setPayTime(LocalDateTime.now());
mobileOrderMapper.updateById(wxOrder);
/* JkStore jkStore = jkStoreMapper.selectById(device.getStoreId());
List<ChildStoreAccount> childStoreAccounts = childStoreAccountMapper.selectList(new LambdaQueryWrapper<ChildStoreAccount>().eq(ChildStoreAccount::getStoreId, jkStore.getId()));
if (CollectionUtils.isEmpty(childStoreAccounts)){
singleFz(wxOrder,jkStore);
}else {
multipleFz(wxOrder,jkStore,childStoreAccounts);
}*/
device.setDeviceId(wxOrder.getDeviceId());
device.setStatus("1");
device.setForcePay(2);
deviceMapper.updateById(device);
try {
redisTemplate.opsForValue().set(UniConstant.DEVICE_ID+device.getDeviceId(),wxOrder.getMobileUserId(),24, TimeUnit.HOURS);
}catch (Exception e){
log.info("将理疗用户加入缓存发生异常,{}",e.getMessage());
}
}
private void singleFz(MobileOrder wxOrder,JkStore jkStore){
StoreAccount storeAccount = storeAccountMapper.selectOne(new LambdaQueryWrapper<StoreAccount>().eq(StoreAccount::getStoreId, jkStore.getId()));
if (Objects.isNull(storeAccount)){
StoreAccount account = new StoreAccount();
account.setStoreId(jkStore.getId());
BigDecimal amount = wxOrder.getTotalPrice().multiply(jkStore.getProfitSharingPercentage()).divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN);
log.info("门店ID->{},分得的金额是:{},=================",jkStore.getId(),amount);
account.setBalance(amount);
account.setAvailableBalance(amount);
account.setFreezeBalance(BigDecimal.valueOf(0.00));
storeAccountMapper.insert(account);
}else {
BigDecimal amount = wxOrder.getTotalPrice().multiply(jkStore.getProfitSharingPercentage()).divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN);
BigDecimal balance = storeAccount.getBalance().add(amount);
BigDecimal avaBalance = storeAccount.getAvailableBalance().add(amount);
log.info("门店ID->{},分得的金额是:{},+++++++++++++++++++",jkStore.getId(),amount);
storeAccount.setBalance(balance);
storeAccount.setAvailableBalance(avaBalance);
storeAccountMapper.updateById(storeAccount);
}
}
private void multipleFz(MobileOrder wxOrder,JkStore jkStore,List<ChildStoreAccount> childStoreAccounts){
StoreAccount storeAccount = storeAccountMapper.selectOne(new LambdaQueryWrapper<StoreAccount>().eq(StoreAccount::getStoreId, jkStore.getId()));
if (Objects.isNull(storeAccount)){
StoreAccount account = new StoreAccount();
account.setStoreId(jkStore.getId());
BigDecimal amount = wxOrder.getTotalPrice().multiply(jkStore.getProfitSharingPercentage()).divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN);
BigDecimal totalPercent = new BigDecimal("0.00");
for (ChildStoreAccount childStoreAccount : childStoreAccounts) {
totalPercent = totalPercent.add(childStoreAccount.getPercentage());
}
BigDecimal surPercent = new BigDecimal("100.00").subtract(totalPercent);
BigDecimal masterStoreAmount = amount.multiply(surPercent).divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN);
log.info("多方分账,主门店门店ID->{},分得的金额是:{},",jkStore.getId(),masterStoreAmount);
account.setBalance(masterStoreAmount);
account.setAvailableBalance(masterStoreAmount);
account.setFreezeBalance(BigDecimal.valueOf(0.00));
storeAccountMapper.insert(account);
for (ChildStoreAccount childStoreAccount : childStoreAccounts) {
BigDecimal b = amount.multiply(childStoreAccount.getPercentage()).divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN);
childStoreAccount.setAccountBalance(childStoreAccount.getAccountBalance().add(b));
}
childStoreAccountMapper.updateById(childStoreAccounts);
}else {
BigDecimal amount = wxOrder.getTotalPrice().multiply(jkStore.getProfitSharingPercentage()).divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN);
BigDecimal totalPercent = new BigDecimal("0.00");
for (ChildStoreAccount childStoreAccount : childStoreAccounts) {
totalPercent = totalPercent.add(childStoreAccount.getPercentage());
}
BigDecimal surPercent = new BigDecimal("100.00").subtract(totalPercent);
BigDecimal masterStoreAmount = amount.multiply(surPercent).divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN);
log.info("多方分账,主门店门店ID->{},分得的金额是:{},",jkStore.getId(),masterStoreAmount);
storeAccount.setBalance(storeAccount.getBalance().add(masterStoreAmount));
storeAccount.setAvailableBalance(storeAccount.getAvailableBalance().add(masterStoreAmount));
storeAccountMapper.updateById(storeAccount);
for (ChildStoreAccount childStoreAccount : childStoreAccounts) {
BigDecimal b = amount.multiply(childStoreAccount.getPercentage()).divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN);
childStoreAccount.setAccountBalance(childStoreAccount.getAccountBalance().add(b));
}
childStoreAccountMapper.updateById(childStoreAccounts);
}
}
private void coursePay(MobileOrder wxOrder){
wxOrder.setStatus(OrderStatus.paid.name());
wxOrder.setPayTime(LocalDateTime.now());
mobileOrderMapper.updateById(wxOrder);
MobileAppUser wxUser = mobileAppUserMapper.selectOne(new LambdaQueryWrapper<MobileAppUser>().eq(MobileAppUser::getId, wxOrder.getMobileUserId()));
MobileUserHaveCourse userHaveCourse = new MobileUserHaveCourse();
userHaveCourse.setBuyTime(LocalDateTime.now());
userHaveCourse.setMobileUserId(wxUser.getId());
userHaveCourse.setCourseId(wxOrder.getCourseId());
userHaveCourse.setStatus(1);
mobileUserHaveCourseMapper.insert(userHaveCourse);
}
public void vipRecharge(MobileOrder wxOrder){
wxOrder.setStatus(OrderStatus.paid.name());
wxOrder.setPayTime(LocalDateTime.now());
mobileOrderMapper.updateById(wxOrder);
WxVipRecharge vipRecharge = vipRechargeMapper.selectOne(new LambdaQueryWrapper<WxVipRecharge>().eq(WxVipRecharge::getOrderId, wxOrder.getOrderId()));
vipRecharge.setPayStatus(1);
vipRechargeMapper.updateById(vipRecharge);
BigDecimal totalAmount = vipRecharge.getRechargeAmount().add(vipRecharge.getPresentAmount());
MobileAppUserAccount wxUserAccount = mobileAppUserAccountMapper.selectOne(new LambdaQueryWrapper<MobileAppUserAccount>().eq(MobileAppUserAccount::getMobileUserId, wxOrder.getMobileUserId()));
if (Objects.isNull(wxUserAccount)){
MobileAppUserAccount account = new MobileAppUserAccount();
account.setAccountBalance(totalAmount);
account.setMobileUserId(wxOrder.getMobileUserId());
mobileAppUserAccountMapper.insert(account);
}else {
wxUserAccount.setAccountBalance(wxUserAccount.getAccountBalance().add(totalAmount));
mobileAppUserAccountMapper.updateById(wxUserAccount);
}
}
}
@@ -0,0 +1,62 @@
package cn.unizk.controller;
import cn.unizk.entity.po.InventoryStatistics;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.entity.vo.UniPage;
import cn.unizk.mapper.InventoryStatisticsMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* @Author hao
* @Date 2026/8/20 10:31
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/inventory")
public class InventoryController {
@Resource
InventoryStatisticsMapper inventoryStatisticsMapper;
@PostMapping("/getStatistics")
public Page<InventoryStatistics> page(@RequestBody UniPage<InventoryStatistics> uniPage){
Page<InventoryStatistics> page = (Page<InventoryStatistics>) uniPage.getPage();
LambdaQueryWrapper<InventoryStatistics> queryWrapper = uniPage.buildQueryWrapper(query -> {
LambdaQueryWrapper<InventoryStatistics> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(query.getType() != null,InventoryStatistics::getType,query.getType());
wrapper.like(StringUtils.isNotBlank(query.getProductName()), InventoryStatistics::getProductName, query.getProductName());
wrapper.orderByDesc(InventoryStatistics::getCreateTime);
return wrapper;
});
return inventoryStatisticsMapper.selectPage(page,queryWrapper);
}
@PostMapping("/addStatistics")
public ResultData addStatistics(@RequestBody InventoryStatistics statistics){
statistics.setRemainingQuantity(statistics.getQuantityInStock() - statistics.getOutboundQuantity());
statistics.setCreateTime(LocalDate.now());
inventoryStatisticsMapper.insert(statistics);
return ResultData.success();
}
@PostMapping("/updateStatistics")
public ResultData updateStatistics(@RequestBody InventoryStatistics statistics){
statistics.setRemainingQuantity(statistics.getQuantityInStock() - statistics.getOutboundQuantity());
inventoryStatisticsMapper.updateById(statistics);
return ResultData.success();
}
@PostMapping("/deleteStatistics")
public ResultData deleteStatistics(@RequestParam("id") Integer id){
inventoryStatisticsMapper.deleteById(id);
return ResultData.success();
}
}
@@ -0,0 +1,78 @@
package cn.unizk.controller;
import cn.unizk.entity.po.JkProduct;
import cn.unizk.entity.po.ProductCarousel;
import cn.unizk.entity.vo.JkProductVO;
import cn.unizk.entity.vo.UniPage;
import cn.unizk.service.JkProductService;
import cn.unizk.service.ProductCarouselService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
/**
* @Author hao
* @Date 2025/8/21 18:57
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/product")
public class JkProductController {
@Resource
private JkProductService productService;
@Resource
private ProductCarouselService carouselService;
@GetMapping("/list")
public Page<JkProduct> list(UniPage uniPage, @RequestParam(value = "dictValue",required = false) String dictValue,
@RequestParam(value = "product",required = false) String product,
@RequestParam(value = "priceSort",required = false) String priceSort,
@RequestParam(value = "saleSort",required = false) String saleSort) {
Page<JkProduct> page = new Page<>();
page.setCurrent(uniPage.getCurrentPage());
page.setSize(uniPage.getPageSize());
boolean priceS = !"desc".equals(priceSort);
boolean saleS = !"desc".equals(saleSort);
return productService.page(page, new LambdaQueryWrapper<JkProduct>()
.eq(JkProduct::getProductStatus, "上架")
.eq(StringUtils.isNotBlank(dictValue),JkProduct::getProductType, dictValue)
.like(StringUtils.isNotBlank(product),JkProduct::getProductName,product)
.gt(JkProduct::getProductStock, 0)
.orderBy(StringUtils.isNotBlank(priceSort),priceS,JkProduct::getProductPrice)
.orderBy(StringUtils.isNotBlank(saleSort),saleS,JkProduct::getSales));
}
@GetMapping("/getExquisite")
public Page<JkProduct> listExquisite(UniPage uniPage){
Page<JkProduct> page = uniPage.getPage();
return productService.page(page,new LambdaQueryWrapper<JkProduct>().eq(JkProduct::getExquisite,"true"));
}
@GetMapping("/carousel")
public List<ProductCarousel> getCarousel(){
return carouselService.list(new LambdaQueryWrapper<ProductCarousel>()
.eq(ProductCarousel::getType,"outer"));
}
@GetMapping("/byId")
public JkProductVO byId(@RequestParam("productId") Integer productId){
JkProduct jkProduct = productService.getById(productId);
if (Objects.isNull(jkProduct)){
return new JkProductVO();
}
List<ProductCarousel> list = carouselService.list(new LambdaQueryWrapper<ProductCarousel>().eq(ProductCarousel::getProductId, productId));
JkProductVO jkProductVO = new JkProductVO();
BeanUtils.copyProperties(jkProduct,jkProductVO);
jkProductVO.setProductCarousels(list);
return jkProductVO;
}
}
@@ -0,0 +1,76 @@
package cn.unizk.controller;
import cn.unizk.entity.po.JkStore;
import cn.unizk.entity.po.SysDict;
import cn.unizk.entity.vo.JkStoreReqVO;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.entity.vo.UniPage;
import cn.unizk.service.JkStoreService;
import cn.unizk.service.SysDictService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* @Author hao
* @Date 2025/8/19 11:28
* @Version 1.0
**/
@RequestMapping("/applet")
@Slf4j
@RestController
public class JkStoreController {
@Resource
private JkStoreService jkStoreService;
@Resource
private SysDictService sysDictService;
@PostMapping("/getStores")
public IPage<JkStore> getStores(@RequestBody UniPage<JkStore> uniPage){
Page<JkStore> page = new Page<>();
page.setCurrent(uniPage.getCurrentPage());
page.setSize(uniPage.getPageSize());
LambdaQueryWrapper<JkStore> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(JkStore::getStatus, "营业");
JkStore jkStore = uniPage.getQueryBody();
if (jkStore != null && StringUtils.isNotBlank(jkStore.getType())){
wrapper.eq(JkStore::getType,jkStore.getType());
}
return jkStoreService.getBaseMapper().selectPage(page, wrapper);
}
@GetMapping("/getStoreById")
public JkStore getStoreById(@RequestParam("storeId") Integer storeId){
return jkStoreService.getById(storeId);
}
/**
*
* @param vo
* @return
*/
@PostMapping("/nearest")
public JkStore nearest(@RequestBody JkStoreReqVO vo){
return null;
}
@GetMapping("/getScanPayPrice")
public ResultData getScanPayPrice(@RequestParam(value = "storeId",required = false) Integer storeId,@RequestParam(value = "deviceId",required = false) String deviceId){
log.info("storeId={},deviceId={}",storeId,deviceId);
SysDict sysDict = sysDictService.getById(26);
ResultData<String> data = new ResultData<>();
data.setCode(200);
data.setData(sysDict.getDictValue());
return data;
}
}
@@ -0,0 +1,37 @@
package cn.unizk.controller;
import cn.unizk.entity.po.SysDict;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.service.SysDictService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @Author hao
* @Date 2026/4/30 11:02
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/member")
public class MemberController {
@Resource
private SysDictService sysDictService;
@GetMapping("/getCost")
public ResultData getCost(){
SysDict activeMember = sysDictService.getOne(new LambdaQueryWrapper<SysDict>().eq(SysDict::getDictType, "active_member"));
ResultData<String> data = new ResultData<>();
if (activeMember == null){
data.setData("888.00");
}else {
data.setData(activeMember.getDictValue());
}
data.setCode(200);
return data;
}
}
@@ -0,0 +1,54 @@
package cn.unizk.controller;
import cn.unizk.entity.po.Joint;
import cn.unizk.entity.po.PhysioThirteenArea;
import cn.unizk.entity.vo.JointVO;
import cn.unizk.mapper.JointMapper;
import cn.unizk.mapper.PhysioThirteenAreaMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* @Author hao
* @Date 2026/9/2 16:43
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/physioArea")
public class Physio13AreaController {
@Resource
private PhysioThirteenAreaMapper physioThirteenAreaMapper;
@Resource
private JointMapper jointMapper;
/* @GetMapping("/list")
public List<PhysioThirteenArea> list(@RequestParam("type") Integer type){
List<PhysioThirteenArea> physioThirteenAreas = physioThirteenAreaMapper.selectList(new LambdaQueryWrapper<PhysioThirteenArea>().eq(PhysioThirteenArea::getType,type));
physioThirteenAreas.forEach( area -> {
area.setCause(null);
area.setImproveMethod(null);
area.setExplanation(null);
area.setPictureUrl(null);
});
return physioThirteenAreas;
}*/
@GetMapping("/list")
public List<JointVO> list() {
return jointMapper.selectJointVOList();
}
@GetMapping("/getOneById")
public PhysioThirteenArea area(@RequestParam("id") Integer id) {
return physioThirteenAreaMapper.selectById(id);
}
}
@@ -0,0 +1,222 @@
package cn.unizk.controller;
import cn.hutool.core.util.IdUtil;
import cn.unizk.entity.po.*;
import cn.unizk.entity.vo.*;
import cn.unizk.exception.ServiceException;
import cn.unizk.mapper.*;
import cn.unizk.utils.BeanCopyUtils;
import cn.unizk.utils.JkSecurityUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;
/**
* @Author hao
* @Date 2026/5/18 17:18
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/appointment")
@Slf4j
public class PhysioAppointmentController {
@Resource
private DailySchedulesMapper dailySchedulesMapper;
@Resource
private MobileUserMedicalVisitInfoMapper mobileUserMedicalVisitInfoMapper;
@Resource
private MobileAppointmentsMapper mobileAppointmentsMapper;
@Resource
private JkPhysioMapper jkPhysioMapper;
@Resource
private JkStoreMapper storeMapper;
@Resource
private TimeSlotsMapper timeSlotsMapper;
@Resource
private MobileAppUserMapper mobileAppUserMapper;
@PostMapping("/getPhysioAppointment")
public List<DailySchedulesVO> getPhysioAppointment(@RequestBody AppointmentVO vo){
return dailySchedulesMapper.getCanAppointment(vo.getPhysioId(),vo.getDateTime());
}
@PostMapping("/appointment")
@Transactional(rollbackFor = Exception.class)
public ResultData appointment(HttpServletRequest request,@RequestBody AppointmentVO2 vo2){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
DailySchedules dailySchedules = dailySchedulesMapper.selectById(vo2.getDailyScheduleId());
if (Objects.isNull(dailySchedules)){
throw new ServiceException("无法预约,请稍后重试");
}
if (dailySchedules.getMaxBookings() - dailySchedules.getBookedCount() < 1){
if ("available".equals(dailySchedules.getStatus())){
dailySchedules.setStatus("full");
dailySchedulesMapper.updateById(dailySchedules);
}
throw new ServiceException("该时间段已满号,请预约其他时间");
}
MobileUserMedicalVisitInfo userMedicalVisitInfo = mobileUserMedicalVisitInfoMapper.selectById(vo2.getMedicalVisitId());
MobileAppointments appointments = new MobileAppointments();
String appointment_no = IdUtil.getSnowflakeNextIdStr();
appointments.setAppointmentNo(appointment_no);
appointments.setMobileUserId(currentUser.getId());
appointments.setDailyScheduleId(vo2.getDailyScheduleId());
appointments.setVisitDate(dailySchedules.getScheduleDate());
appointments.setTimeSlotCode(vo2.getTimeSlotCode());
appointments.setPayAmount(vo2.getPayAmount());
appointments.setPayStatus(1);
appointments.setPhysioId(dailySchedules.getPhysioId());
appointments.setPatientName(userMedicalVisitInfo.getName());
appointments.setPatientPhone(userMedicalVisitInfo.getPhoneNumber());
appointments.setStatus("confirmed");
appointments.setRemark(vo2.getRemark());
mobileAppointmentsMapper.insert(appointments);
if (dailySchedules.getMaxBookings() - dailySchedules.getBookedCount() < 2){
dailySchedules.setStatus("full");
}
dailySchedules.setBookedCount(dailySchedules.getBookedCount() + 1);
dailySchedulesMapper.updateById(dailySchedules);
try {
JkPhysio jkPhysio = jkPhysioMapper.selectById(appointments.getPhysioId());
MobileAppUser user = mobileAppUserMapper.selectById(currentUser.getId());
if (user.getFirstStoreId() == null && user.getFirstPhysioId() == null){
user.setId(currentUser.getId());
user.setFirstPhysioId(jkPhysio.getId());
user.setFirstStoreId(jkPhysio.getStoreId());
mobileAppUserMapper.updateById(user);
}
}catch (Exception e){
log.error("首次门店首次技师绑定失败:",e);
}
return ResultData.success();
}
@PostMapping("/addOrUpdateMedicalVisitInfo")
public ResultData addMedicalVisitInfo(HttpServletRequest request, @RequestBody MobileUserMedicalVisitInfo info){
if (info.getId() == null){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
List<MobileUserMedicalVisitInfo> wxUserMedicalVisitInfos = mobileUserMedicalVisitInfoMapper.selectList(new LambdaQueryWrapper<MobileUserMedicalVisitInfo>().eq(MobileUserMedicalVisitInfo::getMobileUserId, currentUser
.getId()));
if (wxUserMedicalVisitInfos.size() >= 3){
throw new ServiceException("最多只能添加3张就诊卡");
}
info.setMobileUserId(currentUser.getId());
info.setCreateTime(LocalDateTime.now());
mobileUserMedicalVisitInfoMapper.insert(info);
}else {
if (info.getItsDefault() == 1){
mobileUserMedicalVisitInfoMapper.update(new LambdaUpdateWrapper<MobileUserMedicalVisitInfo>().set(MobileUserMedicalVisitInfo::getItsDefault,0));
}
mobileUserMedicalVisitInfoMapper.updateById(info);
}
return ResultData.success();
}
@GetMapping("/getDefaultVisitInfo")
public ResultData getDefVisitInfo(HttpServletRequest request){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
MobileUserMedicalVisitInfo wxUserMedicalVisitInfo = mobileUserMedicalVisitInfoMapper.selectOne(new LambdaQueryWrapper<MobileUserMedicalVisitInfo>().eq(MobileUserMedicalVisitInfo::getMobileUserId, currentUser.getId()).eq(MobileUserMedicalVisitInfo::getItsDefault,1));
ResultData<MobileUserMedicalVisitInfo> resultData = new ResultData<>();
if (Objects.nonNull(wxUserMedicalVisitInfo)){
resultData.setData(wxUserMedicalVisitInfo);
}else {
MobileUserMedicalVisitInfo visitInfo = mobileUserMedicalVisitInfoMapper.selectLatest(currentUser.getId());
resultData.setData(visitInfo);
}
resultData.setCode(200);
return resultData;
}
@DeleteMapping("/deleteVisitInfo")
public ResultData deleteVisitInfo(@RequestParam("id") Integer id){
mobileUserMedicalVisitInfoMapper.deleteById(id);
return ResultData.success();
}
@GetMapping("/getVisitInfo")
public ResultData getVisitInfo(HttpServletRequest request){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
List<MobileUserMedicalVisitInfo> wxUserMedicalVisitInfos = mobileUserMedicalVisitInfoMapper.selectList(new LambdaQueryWrapper<MobileUserMedicalVisitInfo>().eq(MobileUserMedicalVisitInfo::getMobileUserId, currentUser.getId()));
return ResultData.success(wxUserMedicalVisitInfos);
}
@GetMapping("/getAvailableSituation")
public List<AppointmentVO3> getAv(@RequestParam(value = "physioId",required = false) Integer physioId,@RequestParam("storeId") Integer storeId){
List<JkPhysio> jkPhysios = jkPhysioMapper.selectList(new LambdaQueryWrapper<JkPhysio>().eq(JkPhysio::getStoreId, storeId));
List<Integer> physioIds = jkPhysios.stream().map(JkPhysio::getId).collect(Collectors.toList());
List<AppointmentVO3> appointmentVO3s = dailySchedulesMapper.selectMedicalyoumeihao(physioId,physioIds);
for (AppointmentVO3 appointmentVO3 : appointmentVO3s) {
if (appointmentVO3.getScheduleDate().equals(LocalDate.now())){
LocalTime time = LocalTime.now();
TimeSlots timeSlots = timeSlotsMapper.selectLastOne();
if (time.isAfter(timeSlots.getStartTime())){
appointmentVO3.setHasAvailable("无号");
appointmentVO3.setAvailableSlotsCount(0);
appointmentVO3.setTotalAvailableCount(0);
}
}
}
return appointmentVO3s;
}
@PostMapping("/getAllPhysioActive")
public List<AppointmentVO4> getAll(@RequestBody DailySchedulesVO dailySchedules){
if (Objects.isNull(dailySchedules) || dailySchedules.getScheduleDate() == null){
throw new ServiceException("scheduleDate cannot null");
}
if (dailySchedules.getStoreId() == null){
throw new ServiceException("storeId cannot null");
}
List<JkPhysio> jkPhysios = jkPhysioMapper.selectList(new LambdaQueryWrapper<JkPhysio>().eq(JkPhysio::getStoreId, dailySchedules.getStoreId()));
List<Integer> physioIds = jkPhysios.stream().map(JkPhysio::getId).collect(Collectors.toList());
return dailySchedulesMapper.selectYoumeihaoByDate(dailySchedules.getScheduleDate(),physioIds);
}
@GetMapping("/getAppointments")
public Page<AppointmentsVO5> getAppointments(HttpServletRequest request,@RequestParam(value = "currentPage",required = false,defaultValue = "1") Integer currentPage,
@RequestParam(value = "pageSize",required = false,defaultValue = "10") Integer pageSize){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)){
throw new ServiceException(401,"请重新登录");
}
Page<MobileAppointments> page = new Page<>(currentPage,pageSize);
Page<MobileAppointments> appointmentsPage = mobileAppointmentsMapper.selectPage(page, new LambdaQueryWrapper<MobileAppointments>().eq(MobileAppointments::getMobileUserId, currentUser.getId())
.orderByDesc(MobileAppointments::getCreatedAt));
List<MobileAppointments> appointments = appointmentsPage.getRecords();
if (CollectionUtils.isEmpty(appointments)){
return new Page<>();
}
Collection<AppointmentsVO5> appointmentsVO5s = BeanCopyUtils.copyArrays(appointments, AppointmentsVO5::new);
for (AppointmentsVO5 vo5 : appointmentsVO5s) {
JkPhysio jkPhysio = jkPhysioMapper.selectById(vo5.getPhysioId());
JkStore jkStore = storeMapper.selectById(jkPhysio.getStoreId());
vo5.setPhysioName(jkPhysio.getPhysioName());
vo5.setStoreName(jkStore.getStoreName());
vo5.setStoreAddress(jkStore.getAddress());
TimeSlots timeSlots = timeSlotsMapper.selectOne(new LambdaQueryWrapper<TimeSlots>().eq(TimeSlots::getSlotCode, vo5.getTimeSlotCode()));
vo5.setTimeSlots(timeSlots);
}
Page<AppointmentsVO5> vo5Page = new Page<>();
vo5Page.setTotal(page.getTotal());
vo5Page.setRecords((List<AppointmentsVO5>) appointmentsVO5s);
vo5Page.setCurrent(page.getCurrent());
vo5Page.setSize(page.getSize());
return vo5Page;
}
}
@@ -0,0 +1,256 @@
package cn.unizk.controller;
import cn.unizk.entity.po.*;
import cn.unizk.entity.vo.*;
import cn.unizk.exception.ServiceException;
import cn.unizk.huifupay.vo.ReportTypeVO;
import cn.unizk.mapper.*;
import cn.unizk.service.JkPhysioService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @Author hao
* @Date 2025/8/21 16:58
* @Version 1.0
**/
@RestController
@RequestMapping("/applet")
public class PhysioController {
@Resource
private JkPhysioService physioService;
@Resource
private ReportTemplateMapper reportTemplateMapper;
@Resource
private SubHealthDietaryRegulationMapper regulationMapper;
@Resource
private PhysioExamMapper examMapper;
@Resource
private ExamScoreMapper scoreMapper;
@Resource
private MobileOrderMapper mobileOrderMapper;
@Resource
private MobileAppUserMapper mobileAppUserMapper;
@Resource
private TreatParameterMapper treatParameterMapper;
@GetMapping("/getPhysios")
public IPage<JkPhysio> getPhysios(@RequestParam("storeId") Integer storeId, UniPage uniPage) {
Page<JkPhysio> page = new Page<>();
page.setCurrent(uniPage.getCurrentPage());
return physioService.getBaseMapper().selectPage(page, new LambdaQueryWrapper<JkPhysio>()
.eq(JkPhysio::getStoreId, storeId).eq(JkPhysio::getAudit, 1).eq(JkPhysio::getStatus, "启用"));
}
@PostMapping("/getReportById/{id}")
public ReportTemplateVO getReportById(@PathVariable("id") Integer id, @RequestBody ReportTypeVO vo) {
TreatParameter treatParameter = treatParameterMapper.selectById(id);
String text = "<p>尊敬的客户您好!经诊断您可能患有"+treatParameter.getSymptomsName()+",非常理解您正承受着膝关节疼痛带来的不适与困扰," +
"考虑到您目前关节疼痛症状较为严重,为了能精准对症缓解您的病痛," +
"我们现将您的相关情况转接至上一级专业接待机构。那边配备经验更丰富的护理人员与配" +
"套诊疗资源,会为您提供更全面、专业且细致优质的诊疗服务,请您稍作等候配合转接。</p>";
ReportTemplateVO templateVO = new ReportTemplateVO();
if (vo.getResult() == 2) {
templateVO.setType(vo.getResult());
templateVO.setId(randomNum());
templateVO.setText(text);
return templateVO;
}
if (vo.getResult() == 1) {
ReportTemplate reportTemplate = reportTemplateMapper.selectById(id);
BeanUtils.copyProperties(reportTemplate, templateVO);
templateVO.setType(vo.getResult());
return templateVO;
}
throw new ServiceException("系统异常");
}
private Integer randomNum() {
List<Integer> numbers = new ArrayList<>();
for (int i = 1; i <= 13; i++) {
numbers.add(i);
}
// 2. 打乱列表顺序
Collections.shuffle(numbers);
return numbers.get(0);
}
@GetMapping("/getRegulation")
public List<SubHealthDietaryRegulation> getRegulation(@RequestParam(value = "name", required = false) String name) {
return regulationMapper.selectList(new LambdaQueryWrapper<SubHealthDietaryRegulation>()
.like(StringUtils.isNotBlank(name), SubHealthDietaryRegulation::getSymptomDescription, name));
}
@PostMapping("/getFoodTherapy")
public ResultData getFoodTherapy(@RequestBody RegulationVO vo) {
if (Objects.isNull(vo) || CollectionUtils.isEmpty(vo.getNumbers())) {
return null;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ResultData<String> data = new ResultData<>();
data.setCode(200);
String s = "根据您的症状以及过往病史的描述,您可能由于{reason}的原因引起的,并且身体可能缺乏{n},建议您做一下{z},更精准地查出病因。日常食疗建议多补充{f}。{s}";
List<String> numbers = vo.getNumbers();
if (numbers.size() == 1) {
SubHealthDietaryRegulation regulation = regulationMapper.selectById(vo.getNumbers().get(0));
String f = regulation.getFruitSuggestion() + ";" + regulation.getVegetableSuggestion();
String result = s.replace("{reason}", regulation.getCommonReasons()).replace("{n}", regulation.getLackNutrients()).replace("{z}", regulation.getDiagnosticReference())
.replace("{f}", f).replace("{s}", regulation.getDetectAndAdd());
data.setData(result);
} else {
List<SubHealthDietaryRegulation> regulations = regulationMapper.selectByIds(numbers);
List<String> reasons = new ArrayList<>();
List<String> zd = new ArrayList<>();
List<String> ns = new ArrayList<>();
List<String> sgs = new ArrayList<>();
List<String> vgs = new ArrayList<>();
List<String> jcbc = new ArrayList<>();
for (SubHealthDietaryRegulation regulation : regulations) {
String commonReasons = regulation.getCommonReasons();
String diagnosticReference = regulation.getDiagnosticReference();
String vegetableSuggestion = regulation.getVegetableSuggestion();
String fruitSuggestion = regulation.getFruitSuggestion();
@NotBlank(message = "lackNutrients cannot be empty") String lackNutrients = regulation.getLackNutrients();
hb(commonReasons, reasons);
hb(diagnosticReference, zd);
hb(vegetableSuggestion, vgs);
hb(fruitSuggestion, sgs);
hb(lackNutrients, ns);
@NotBlank(message = "detectAndAdd cannot be empty") String detectAndAdd = regulation.getDetectAndAdd();
String replace = detectAndAdd.replace("检测:", "").replace("补充:", "");
jcbc.add(replace);
}
String r = String.join("、", reasons);
String n = String.join("、", ns);
String z = String.join("、", zd);
sgs.addAll(vgs);
String f = String.join("、", sgs);
String jb = "微量元素检测与补充:" + String.join(";", jcbc);
String replace = s.replace("{reason}", r).replace("{n}", n).replace("{z}", z).replace("{f}", f).replace("{s}", jb);
data.setData(replace);
}
return data;
}
private void hb(String s, List<String> list) {
if (s.contains("、")) {
String[] split = s.split("、");
List<String> item = new ArrayList<>();
for (String t : split) {
if (!list.contains(t)) {
item.add(t);
}
}
list.addAll(item);
} else {
if (!list.contains(s)) {
list.add(s);
}
}
}
@PostMapping("/physioExams")
public List<PhysioExamVO> physioExams(@RequestParam("subject") Integer subject) {
List<PhysioExam> physioExams = examMapper.selectList(new LambdaQueryWrapper<PhysioExam>()
.eq(PhysioExam::getSubject, subject));
if (CollectionUtils.isEmpty(physioExams)) {
return new ArrayList<>();
}
List<PhysioExamVO> vos = new ArrayList<>();
for (PhysioExam physioExam : physioExams) {
PhysioExamVO vo = new PhysioExamVO();
vo.setId(physioExam.getId());
vo.setType(physioExam.getType());
vo.setAnswers(Arrays.asList(physioExam.getAnswer().split("&")));
vo.setTitle(physioExam.getTitle());
vos.add(vo);
}
return vos;
}
@PostMapping("/gradeExam")
public Integer gradeExam(@RequestBody List<GradeExamVO> examVOS) {
if (CollectionUtils.isEmpty(examVOS)) {
return 0;
}
List<Integer> collect = examVOS.stream().map(GradeExamVO::getExamId)
.collect(Collectors.toList());
List<PhysioExam> physioExams = examMapper.selectByIds(collect);
Map<Integer, PhysioExam> map = physioExams.stream()
.collect(Collectors.toMap(PhysioExam::getId, Function.identity()));
List<Integer> list = new ArrayList<>();
for (GradeExamVO examVO : examVOS) {
if (StringUtils.isBlank(examVO.getAnswer())) {
continue;
}
if (map.get(examVO.getExamId()).getCorrectAnswer().equals(examVO.getAnswer())) {
list.add(map.get(examVO.getExamId()).getType());
}
}
if (CollectionUtils.isEmpty(list)) {
return 0;
}
List<Integer> distInt = list.stream().distinct().collect(Collectors.toList());
// examScoreMapper.selectByIds(distInt),查出examScore,再统计分数。
List<ExamScore> scoreList = scoreMapper.selectByIds(distInt);
Map<Integer, Integer> scoreMap = scoreList.stream().collect(Collectors.toMap(ExamScore::getId, ExamScore::getScore));
int totalScore = 0;
for (Integer integer : list) {
Integer score = scoreMap.get(integer);
totalScore += score;
}
return totalScore;
}
@GetMapping("/getChuFang")
public ResultData<List<TreatParameter>> getChuFang(@RequestParam("physioId") Integer physioId){
List<MobileOrder> wxOrders = mobileOrderMapper.selectList(new LambdaQueryWrapper<MobileOrder>().eq(MobileOrder::getPhysioId, physioId));
if (CollectionUtils.isEmpty(wxOrders)){
return ResultData.success();
}
List<Integer> openids = wxOrders.stream().map(MobileOrder::getMobileUserId).distinct().collect(Collectors.toList());
List<MobileAppUser> wxUsers = mobileAppUserMapper.selectList(new LambdaQueryWrapper<MobileAppUser>().in(MobileAppUser::getId, openids));
if (CollectionUtils.isEmpty(wxUsers)){
return ResultData.success();
}
List<String> collect = wxUsers.stream().filter(u -> StringUtils.isNotBlank(u.getPainNumber())).map(MobileAppUser::getPainNumber).collect(Collectors.toList());
List<TreatParameter> list = new ArrayList<>();
for (String s : collect) {
if (s.contains(",")){
String[] split = s.split(",");
for (String p : split) {
TreatParameter treatParameter = treatParameterMapper.selectById(p);
list.add(treatParameter);
}
}else {
TreatParameter treatParameter = treatParameterMapper.selectById(s);
list.add(treatParameter);
}
}
ResultData<List<TreatParameter>> data = new ResultData<>();
data.setCode(200);
data.setData(list);
return data;
}
}
@@ -0,0 +1,441 @@
package cn.unizk.controller;
import cn.unizk.entity.dto.MajorJointsDTO;
import cn.unizk.entity.po.DiaRecord;
import cn.unizk.entity.po.MobileAppUser;
import cn.unizk.entity.vo.EightMajorJointsVO;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.entity.vo.SpeakVO;
import cn.unizk.exception.ServiceException;
import cn.unizk.mapper.DiaRecordMapper;
import cn.unizk.mapper.MobileAppUserMapper;
import cn.unizk.utils.JkSecurityUtil;
import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;
/**
* @Author hao
* @Date 2026/8/10 10:56
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/rating")
public class RatingController {
@Resource
private MobileAppUserMapper mobileAppUserMapper;
@Resource
private DiaRecordMapper diaRecordMapper;
private static final Map<Integer, MajorJointsDTO> JOINTS_DTO_MAP1;
private static final Map<Integer, MajorJointsDTO> JOINTS_DTO_MAP2;
private static final Map<Integer,MajorJointsDTO> WAN_ZHOU_MAP;
private static final Map<Integer,MajorJointsDTO> YAO_MAP;
static {
JOINTS_DTO_MAP1 = new HashMap<>();
MajorJointsDTO dto = new MajorJointsDTO();
Map<Integer,String> questionMap1 = new HashMap<>();
questionMap1.put(1,"1.肩膀前面锁骨下面凹窝处会不会闷痛、按压疼?");
questionMap1.put(2,"2.抬手向前举胳膊、抱重物时,肩膀前侧扯着刺痛吗?");
questionMap1.put(3,"3.胳膊向后背、穿脱套头衫时,肩膀前面牵拉难受吗?");
dto.setTitle("肩前侧锁骨下方");
dto.setReport("肩膀前侧肌腱劳损、肩关节前侧滑膜发炎,长期抬手干活、抱重物诱发");
dto.setMap(questionMap1);
JOINTS_DTO_MAP1.put(1,dto);
MajorJointsDTO dto2 = new MajorJointsDTO();
dto2.setTitle("肩顶端凸起骨头");
dto2.setReport("肩峰滑囊炎、肩袖上方肌腱磨损,长期抬手、侧睡压肩引发");
Map<Integer,String> questionMap2 = new HashMap<>();
questionMap2.put(1,"1.肩膀最顶端那块骨头,一按就酸胀疼吗?");
questionMap2.put(2,"2.胳膊侧平举、举过头顶时,肩膀顶刺痛卡住吗?");
questionMap2.put(3,"3.侧卧压着肩膀睡觉,第二天肩顶疼痛感明显加重吗?");
dto2.setMap(questionMap2);
JOINTS_DTO_MAP1.put(2,dto2);
MajorJointsDTO dto3 = new MajorJointsDTO();
dto3.setTitle("肩膀外侧三角肌整块");
dto3.setReport("肩部外侧肌肉劳损、肩袖外侧轻微损伤,单侧负重、长期外展发力导致");
Map<Integer,String> questionMap3 = new HashMap<>();
questionMap3.put(1,"1.肩膀外侧一大块肌肉经常发酸发沉吗?");
questionMap3.put(2,"2.向外抬胳膊、拎手提东西,外侧牵拉酸痛吗?");
questionMap3.put(3,"运动干活后,肩膀外侧发胀,抬胳膊无力吗?");
dto3.setMap(questionMap3);
JOINTS_DTO_MAP1.put(3,dto3);
MajorJointsDTO dto4 = new MajorJointsDTO();
dto4.setTitle("肩膀后侧肩胛骨");
dto4.setReport("肩后筋膜僵硬、肩关节后侧韧带劳损,含胸驼背、后背内旋动作诱发");
Map<Integer,String> questionMap4 = new HashMap<>();
questionMap4.put(1,"后背肩膀骨头边缘隐隐拉扯酸胀吗?");
questionMap4.put(2,"手往后背、反手抓后背腰带,肩膀后侧刺痛吗?");
questionMap4.put(3,"含胸久坐、低头玩手机,肩后发紧僵硬疼吗?");
dto4.setMap(questionMap4);
JOINTS_DTO_MAP1.put(4,dto4);
MajorJointsDTO dto5 = new MajorJointsDTO();
dto5.setTitle("肩胛骨内侧缝");
dto5.setReport("肩胛内侧筋膜劳损、菱形肌紧张,长期久坐低头、圆肩驼背造成");
Map<Integer,String> questionMap5 = new HashMap<>();
questionMap5.put(1,"1.后背两块肩胛骨中间、靠近脖子那条缝酸胀疼吗?");
questionMap5.put(2,"久坐伏案、低头看手机,肩缝发沉像压了重物吗?");
questionMap5.put(3,"胳膊向前环抱、含胸,肩内侧牵扯刺痛吗?");
dto5.setMap(questionMap5);
JOINTS_DTO_MAP1.put(5,dto5);
MajorJointsDTO dto6 = new MajorJointsDTO();
dto6.setTitle("腋下肩胛内侧区");
dto6.setReport("肩峰撞击、长头腱炎症、肩胛下肌损伤");
Map<Integer,String> questionMap6 = new HashMap<>();
questionMap6.put(1,"后背手腋下深处疼吗?");
questionMap6.put(2,"屈肘内旋用力腋下酸胀?");
questionMap6.put(3,"侧卧夹臂睡觉会不会闷痛?");
dto6.setMap(questionMap6);
JOINTS_DTO_MAP1.put(6,dto6);
}
//手指
static {
JOINTS_DTO_MAP2 = new HashMap<>();
MajorJointsDTO dto = new MajorJointsDTO();
Map<Integer,String> questionMap1 = new HashMap<>();
questionMap1.put(1,"大拇指整根手指、指关节、根部位置,按压有没有酸胀、刺痛、发硬的感觉?");
questionMap1.put(2,"频繁划手机、捏东西、开门锁、捏筷子发力时,大拇指会不会扯着疼?");
questionMap1.put(3,"大拇指弯曲、伸直的时候,有没有卡顿、发僵、活动不顺畅的情况?");
dto.setTitle("拇指区");
dto.setReport("拇指腱鞘劳损、拇指关节疲劳、拇指肌腱卡压");
dto.setMap(questionMap1);
JOINTS_DTO_MAP2.put(1,dto);
MajorJointsDTO dto2 = new MajorJointsDTO();
dto2.setTitle("食指区");
dto2.setReport("食指关节劳损、食指腱鞘轻微发炎、指关节筋膜紧张");
Map<Integer,String> questionMap2 = new HashMap<>();
questionMap2.put(1,"食指整根手指、各个关节按压有没有酸痛、隐痛、刺痛感?");
questionMap2.put(2,"打字、写字、点屏幕、捏取细小物品时,食指发力会不会牵扯疼痛?");
questionMap2.put(3,"食指用力弯曲、伸直、单独抬起时,有没有僵硬、扯痛的感觉?");
dto2.setMap(questionMap2);
JOINTS_DTO_MAP2.put(2,dto2);
MajorJointsDTO dto3 = new MajorJointsDTO();
dto3.setTitle("中指区");
dto3.setReport("中指肌腱劳损、指关节压力性疲劳、腱鞘紧张");
Map<Integer,String> questionMap3 = new HashMap<>();
questionMap3.put(1,"中指各关节、手指整条位置按压,有没有酸胀、压痛的情况?");
questionMap3.put(2,"握拳发力、拎东西、写字按压、干活用力时,中指会不会牵扯疼?");
questionMap3.put(3,"中指弯曲伸直、大幅度活动时,有没有发紧、卡顿、不舒服的感觉?");
dto3.setMap(questionMap3);
JOINTS_DTO_MAP2.put(3,dto3);
MajorJointsDTO dto4 = new MajorJointsDTO();
dto4.setTitle("无名指区");
dto4.setReport("无名指关节劳损、腱鞘循环不畅、手指筋膜紧张");
Map<Integer,String> questionMap4 = new HashMap<>();
questionMap4.put(1,"无名指整条手指、关节位置按压,有没有隐隐酸痛、发胀的感觉?");
questionMap4.put(2,"握拳、抓握物品、抬手发力时,无名指会不会跟着牵扯隐痛?");
questionMap4.put(3,"手指并拢、分开活动时,无名指有没有发紧、活动受限、轻微刺痛?");
dto4.setMap(questionMap4);
JOINTS_DTO_MAP2.put(4,dto4);
MajorJointsDTO dto5 = new MajorJointsDTO();
dto5.setTitle("小指区");
dto5.setReport("小指腱鞘劳损、小指关节牵拉损伤、末梢循环不畅");
Map<Integer,String> questionMap5 = new HashMap<>();
questionMap5.put(1,"小拇指整条手指、关节按压,有没有酸痛、刺痛、发麻发木的感觉?");
questionMap5.put(2,"撑手、趴桌子、手部偏向小指侧用力时,小拇指会不会扯着疼?");
questionMap5.put(3,"小指弯曲伸直、单独活动时,有没有僵硬、不灵活、牵扯不适?");
dto5.setMap(questionMap5);
JOINTS_DTO_MAP2.put(5,dto5);
MajorJointsDTO dto6 = new MajorJointsDTO();
dto6.setTitle("屈肌总腱鞘区");
dto6.setReport("手部总腱鞘劳损、掌心筋膜紧张、手指屈肌疲劳,多由双手长期重复抓握、持续发力、受凉僵硬导致,是多手指整体劳损的核心区域");
Map<Integer,String> questionMap6 = new HashMap<>();
questionMap6.put(1,"手指根部、掌心上方整片位置,按压有没有发紧、发胀、闷痛的感觉?");
questionMap6.put(2," 用力握拳、持续抓东西、反复屈伸所有手指时,整片区域会不会牵扯疼?");
questionMap6.put(3,"长时间握鼠标、玩手机、干活后,掌心手指根部会不会发硬、发僵?");
dto6.setMap(questionMap6);
JOINTS_DTO_MAP2.put(6,dto6);
}
//腕肘
static {
WAN_ZHOU_MAP = new HashMap<>();
MajorJointsDTO dto = new MajorJointsDTO();
Map<Integer,String> questionMap1 = new HashMap<>();
questionMap1.put(1,"手肘外侧突出的小骨头,一按就酸痛吗?");
questionMap1.put(2,"拧毛巾、拎重物、拿水杯发力时,外侧骨头扯着刺痛吗?");
questionMap1.put(3,"长时间打字、拖地、反复抬手,手肘外侧发酸发沉吗?");
dto.setTitle("手肘外侧骨头凸起");
dto.setReport("手肘外侧肌腱劳损、网球肘,反复抓握、拧转发力诱发");
dto.setMap(questionMap1);
WAN_ZHOU_MAP.put(1,dto);
MajorJointsDTO dto2 = new MajorJointsDTO();
dto2.setTitle("手肘内侧骨头凸起");
dto2.setReport("手肘内侧肌腱发炎、高尔夫肘,长期屈肘抓握、支撑发力导致");
Map<Integer,String> questionMap2 = new HashMap<>();
questionMap2.put(1,"手肘内侧凸起骨头按压闷痛吗?");
questionMap2.put(2,"用力攥拳头、屈肘搬东西,手肘内侧牵扯疼吗?");
questionMap2.put(3,"长期屈肘玩手机、趴着撑胳膊,内侧酸胀加重吗?");
dto2.setMap(questionMap2);
WAN_ZHOU_MAP.put(2,dto2);
MajorJointsDTO dto3 = new MajorJointsDTO();
dto3.setTitle("手肘后侧鹰嘴骨头");
dto3.setReport("肘后滑囊发炎、胳膊后侧肌腱劳损,长期支撑、完全伸肘发力诱发");
Map<Integer,String> questionMap3 = new HashMap<>();
questionMap3.put(1,"1.手肘后面凸起那块骨头,撑桌面按压疼吗?");
questionMap3.put(2,"2.完全伸直、用力向后顶东西,肘后刺痛吗?");
questionMap3.put(3,"3.长期趴着睡觉、硬桌子撑手肘,后侧发胀红肿吗?");
dto3.setMap(questionMap3);
WAN_ZHOU_MAP.put(3,dto3);
MajorJointsDTO dto4 = new MajorJointsDTO();
dto4.setTitle("手腕外侧");
dto4.setReport("拇指腱鞘发炎、手腕外侧肌腱劳损,频繁用大拇指发力造成");
Map<Integer,String> questionMap4 = new HashMap<>();
questionMap4.put(1,"1.手腕靠近大拇指的凸起骨头按压酸胀吗?");
questionMap4.put(2,"拧瓶盖、抱小孩、反复划手机,这个位置扯着疼吗?");
questionMap4.put(3,"3.大拇指用力张开、握拳发力,手腕外侧刺痛吗?");
dto4.setMap(questionMap4);
WAN_ZHOU_MAP.put(4,dto4);
MajorJointsDTO dto5 = new MajorJointsDTO();
dto5.setTitle("手腕掌心内侧正中");
dto5.setReport("腕管受压、手腕内侧筋膜劳损,长期弯曲手腕、持续抓握诱发");
Map<Integer,String> questionMap5 = new HashMap<>();
questionMap5.put(1,".手掌根手腕内侧窝窝发闷胀痛吗?");
questionMap5.put(2,"2.长时间屈腕玩手机、握鼠标,手指发麻连带手腕疼吗?");
questionMap5.put(3,"3.睡觉手腕弯着,早上醒来手指僵硬、发麻吗?");
dto5.setMap(questionMap5);
WAN_ZHOU_MAP.put(5,dto5);
MajorJointsDTO dto6 = new MajorJointsDTO();
dto6.setTitle("手腕内侧");
dto6.setReport("手腕侧边韧带劳损、尺骨茎突磨损,手腕偏斜负重、撑地发力导致");
Map<Integer,String> questionMap6 = new HashMap<>();
questionMap6.put(1,"1.手腕靠近小拇指这边骨头按压疼吗?");
questionMap6.put(2,"2.手掌往小指方向歪、撑地起身,手腕侧边刺痛吗?");
// questionMap6.put(3,"侧卧夹臂睡觉会不会闷痛?");
dto6.setMap(questionMap6);
WAN_ZHOU_MAP.put(6,dto6);
MajorJointsDTO dto7 = new MajorJointsDTO();
dto7.setTitle("手腕掌心中间整片");
dto7.setReport("手指屈肌腱劳损、掌心筋膜紧张,长期重复抓握、受凉诱发");
Map<Integer,String> qu7 = new HashMap<>();
qu7.put(1,"手腕掌心连接手指的整片位置发紧酸胀吗?");
qu7.put(2,"2.用力握拳、反复屈伸手指,手腕掌心牵扯疼吗?");
qu7.put(3,"冷水洗手、受凉后,握拳僵硬疼痛感加重吗?");
dto7.setMap(qu7);
WAN_ZHOU_MAP.put(7,dto7);
}
//腰
static {
YAO_MAP = new HashMap<>();
MajorJointsDTO dto = new MajorJointsDTO();
Map<Integer,String> questionMap1 = new HashMap<>();
questionMap1.put(1,"肋骨下方、后腰上方这块经常发酸发胀吗?");
questionMap1.put(2,"2.长时间低头久坐、弯腰干活之后,这里疼得更明显吗?");
questionMap1.put(3,"3.挺直腰、向后舒展后腰时,这块有拉扯难受的感觉吗?");
dto.setTitle("上腰段");
dto.setReport("腰背筋膜上段、后腰浅层肌肉劳损,长期驼背、受凉、扭腰引发代偿酸痛");
dto.setMap(questionMap1);
YAO_MAP.put(1,dto);
MajorJointsDTO dto2 = new MajorJointsDTO();
dto2.setTitle("中下腰椎");
dto2.setReport("腰肌劳损、腰椎关节错位、轻度腰间盘膨出");
Map<Integer,String> questionMap2 = new HashMap<>();
questionMap2.put(1,"1.腰带对应的后腰位置持续发酸、发疼吗?");
questionMap2.put(2,"2.弯腰捡东西、久坐刚站起来的一瞬间,后腰会刺痛吗?");
questionMap2.put(3,"3.用手按压后腰骨头两边,肌肉发硬、一按就疼吗?");
dto2.setMap(questionMap2);
YAO_MAP.put(2,dto2);
MajorJointsDTO dto3 = new MajorJointsDTO();
dto3.setTitle("腰骶结合处");
dto3.setReport("腰底韧带劳损、腰五骶一椎间盘不适,翘腿、拖地、骨盆前倾诱发疼痛");
Map<Integer,String> questionMap3 = new HashMap<>();
questionMap3.put(1,"1.腰最底下、屁股上方凹进去的地方疼吗?");
questionMap3.put(2,"久坐、躺床上翻身的时候,这块扯着牵拉疼吗?");
questionMap3.put(3,"走路久了腰底部发沉,还连带屁股一起酸胀吗?");
dto3.setMap(questionMap3);
YAO_MAP.put(3,dto3);
MajorJointsDTO dto4 = new MajorJointsDTO();
dto4.setTitle("腰椎棘突正中");
dto4.setReport("后腰中间韧带发炎、长期弓背塌腰造成韧带劳损");
Map<Integer,String> questionMap4 = new HashMap<>();
questionMap4.put(1,"1.后腰正中间这条骨头,用手按压有痛感吗?");
questionMap4.put(2,"长时间弯腰,或是刻意向后仰腰时,骨头缝刺痛吗?");
questionMap4.put(3,"3.睡硬床平躺,后腰中间被硌到后难受加重吗?");
dto4.setMap(questionMap4);
YAO_MAP.put(4,dto4);
MajorJointsDTO dto5 = new MajorJointsDTO();
dto5.setTitle("腰侧方");
dto5.setReport("腰侧肌肉痉挛、歪坐、高低腿导致单侧腰部代偿劳损");
Map<Integer,String> questionMap5 = new HashMap<>();
questionMap5.put(1,"1.左右两侧腰窝经常拉扯酸胀吗?");
questionMap5.put(2,"单侧侧身弯腰、单手提重物时,一边腰扯着疼吗?");
questionMap5.put(3,"3.长期侧躺睡觉,受压一侧腰部僵硬酸痛吗?");
dto5.setMap(questionMap5);
YAO_MAP.put(5,dto5);
MajorJointsDTO dto6 = new MajorJointsDTO();
dto6.setTitle(" 髋前侧腹股沟");
dto6.setReport("大腿根胯部肌肉紧张、髋关节前侧滑膜发炎,久蹲、爬坡诱发疼痛");
Map<Integer,String> questionMap6 = new HashMap<>();
questionMap6.put(1,"大腿根里面凹陷处闷闷胀痛吗?");
questionMap6.put(2,"2.深蹲、抬腿、久坐起身时,大腿根扯着刺痛吗?");
questionMap6.put(3,"3.做弓步向前压腿,胯骨前面牵拉难受吗?");
dto6.setMap(questionMap6);
YAO_MAP.put(6,dto6);
MajorJointsDTO dto7 = new MajorJointsDTO();
dto7.setTitle("髋外侧大转子");
dto7.setReport("胯外侧滑囊发炎、臀部外侧肌肉劳损,长期侧睡、走路外八引发");
Map<Integer,String> questionMap7 = new HashMap<>();
questionMap7.put(1,"胯外侧突出的那块骨头,轻轻按压就疼吗?");
questionMap7.put(2,"2.侧躺压住胯骨时,刺痛感会明显加重吗?");
questionMap7.put(3,"走路、上下台阶,胯外侧酸胀还放射到大腿吗?");
dto7.setMap(questionMap7);
YAO_MAP.put(7,dto7);
MajorJointsDTO dto8 = new MajorJointsDTO();
dto8.setTitle("髋后侧臀深处");
dto8.setReport("臀部深层肌肉痉挛、坐骨神经受刺激,久坐盘腿诱发不适");
Map<Integer,String> questionMap8 = new HashMap<>();
questionMap8.put(1,"1.屁股里面深处隐隐胀痛吗?");
questionMap8.put(2,"2.久坐、跷二郎腿之后,屁股深处一条筋扯着大腿后侧发麻疼吗?");
questionMap8.put(3,"3.拉伸臀部的时候,深层有明显牵拉痛感吗?");
dto8.setMap(questionMap8);
YAO_MAP.put(8,dto8);
}
@PostMapping("/score")
public ResultData<String> score(HttpServletRequest request,@RequestBody List<SpeakVO> vos){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)){
throw new ServiceException(401,"登录已过期");
}
List<SpeakVO> collect = vos.stream().filter(vo -> vo.getNo() == 3).collect(Collectors.toList());
if (CollectionUtils.isEmpty(collect)){
throw new ServiceException("数据错误");
}
SpeakVO speakVO = collect.get(0);
String join = String.join(",", speakVO.getOptions());
MobileAppUser appUser = mobileAppUserMapper.selectById(currentUser.getId());
appUser.setTherapy(join);
mobileAppUserMapper.updateById(appUser);
return ResultData.success();
}
@PostMapping("/save4SRating")
public ResultData save4S(HttpServletRequest request,@RequestBody MobileAppUser user){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)){
throw new ServiceException(401,"登录已过期");
}
MobileAppUser appUser = mobileAppUserMapper.selectById(currentUser.getId());
appUser.setFoursRating(user.getFoursRating());
appUser.setEvaluationResult(user.getEvaluationResult());
mobileAppUserMapper.updateById(appUser);
return ResultData.success();
}
@GetMapping("/getRating")
public ResultData<MobileAppUser> getRating(HttpServletRequest request){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)){
throw new ServiceException(401,"登录已过期");
}
MobileAppUser appUser = mobileAppUserMapper.selectById(currentUser.getId());
ResultData<MobileAppUser> data = new ResultData<>();
data.setData(appUser);
data.setCode(200);
return data;
}
private Map<Integer, JSONObject> scoreMap(){
Map<Integer, JSONObject> map = new HashMap<>();
JSONObject one = new JSONObject();
one.put("A",2);
one.put("B",8);
one.put("C",12);
map.put(1,one);
JSONObject two = new JSONObject();
two.put("A",0);
two.put("B",4);
two.put("C",15);
map.put(2,two);
JSONObject three = new JSONObject();
three.put("A",0);
three.put("B",4);
three.put("C",15);
three.put("D",0);
three.put("E",0);
map.put(2,three);
return map;
}
@PostMapping("/eightMajorJoints")
public ResultData eightMajorJoints(HttpServletRequest request,@RequestBody EightMajorJointsVO vo){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)){
throw new ServiceException(401,"登录已过期");
}
List<DiaRecord> records = new ArrayList<>();
String s = RandomStringUtils.randomNumeric(16);
MajorJointsDTO dto = JOINTS_DTO_MAP1.get(vo.getArea());
Map<Integer, String> map = dto.getMap();
List<EightMajorJointsVO.Joints> joints = vo.getJoints();
joints.forEach(j -> {
DiaRecord diaRecord = new DiaRecord();
diaRecord.setPart(vo.getPart());
diaRecord.setArea(dto.getTitle());
diaRecord.setQuestion(map.get(j.getNumber()));
diaRecord.setBatchNumber(s);
diaRecord.setResponse(j.getPatientResponse());
diaRecord.setAppUserId(currentUser.getId());
records.add(diaRecord);
});
diaRecordMapper.insert(records);
return ResultData.success();
}
@PostMapping("/getReport")
public ResultData<String> getReport(@RequestBody EightMajorJointsVO vo){
ResultData<String> data = new ResultData<>();
data.setCode(200);
switch (vo.getPart()){
case "肩关节":
MajorJointsDTO dto = JOINTS_DTO_MAP1.get(vo.getArea());
data.setData(dto.getReport());
return data;
case "腱鞘指关节":
MajorJointsDTO dto1 = JOINTS_DTO_MAP2.get(vo.getArea());
data.setData(dto1.getReport());
return data;
case "腕肘关节":
MajorJointsDTO dto2 = WAN_ZHOU_MAP.get(vo.getArea());
data.setData(dto2.getReport());
return data;
case "腰髋关节":
MajorJointsDTO dto3 = YAO_MAP.get(vo.getArea());
data.setData(dto3.getReport());
return data;
}
return ResultData.success();
}
}
@@ -0,0 +1,65 @@
package cn.unizk.controller;
import cn.unizk.entity.po.RunConsultation;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.exception.ServiceException;
import cn.unizk.mapper.RunConsultationMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @Author hao
* @Date 2026/7/15 11:02
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/running")
public class RunningController {
@Resource
private RunConsultationMapper runConsultationMapper;
@GetMapping("/getRunConsults")
public Page<RunConsultation> getRunConsult(@RequestParam("currentPage") Integer currentPage,
@RequestParam("pageSize") Integer pageSize) {
Page<RunConsultation> page = new Page<>(currentPage, pageSize);
return runConsultationMapper.selectPage(page, null);
}
@PostMapping("/getRunConsultByIds")
public RunConsultation getRunConsultByIds(@RequestBody List<Integer> ids) {
if (CollectionUtils.isEmpty(ids)) {
throw new ServiceException("参数ids不能为空");
}
List<RunConsultation> runConsultations = runConsultationMapper.selectByIds(ids);
RunConsultation consultation = new RunConsultation();
String lesion = runConsultations.stream().map(RunConsultation::getLesion).distinct().collect(Collectors.joining(";"));
consultation.setLesion(lesion);
String conclusion = runConsultations.stream().map(RunConsultation::getConclusion).distinct().collect(Collectors.joining("\n"));
consultation.setConclusion(conclusion);
List<RunConsultation> distinctConsult = runConsultations.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(
RunConsultation::getLesion,
Function.identity(),
(existing, replacement) -> existing,
LinkedHashMap::new
),
map -> new ArrayList<>(map.values())
));
String plan = distinctConsult.stream().map(RunConsultation::getPlan).distinct().collect(Collectors.joining("\n"));
consultation.setPlan(plan);
return consultation;
}
}
@@ -0,0 +1,50 @@
package cn.unizk.controller;
import cn.unizk.entity.po.SysDict;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.service.SysDictService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Author hao
* @Date 2025/9/3 11:30
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/dict")
public class SysDictController {
@Resource
private SysDictService sysDictService;
@GetMapping("/getDictByType")
public List<SysDict> getDictByType(@RequestParam("type") String type){
return sysDictService.list(new LambdaQueryWrapper<SysDict>()
.eq(SysDict::getDictType,type)
.eq(SysDict::getStatus,"display"));
}
@GetMapping("/getDictType")
public List<String> getDictType(){
return sysDictService.list().stream().map(SysDict::getDictType)
.distinct()
.collect(Collectors.toList());
}
/*@GetMapping("/getGrfScanPrice")
public ResultData getGrfScanPrice(){
SysDict sysDict = sysDictService.getById(27);
ResultData<String> data = new ResultData<>();
data.setCode(200);
data.setData(sysDict.getDictValue());
return data;
}*/
}
@@ -0,0 +1,45 @@
package cn.unizk.controller;
import cn.unizk.entity.po.PhysioThirteenArea;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.mapper.PhysioThirteenAreaMapper;
import com.google.common.collect.Lists;
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.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* @Author hao
* @Date 2026/9/2 16:53
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/test")
public class TestController {
@Resource
private PhysioThirteenAreaMapper physioThirteenAreaMapper;
@PostMapping("/add13Area")
public ResultData add13Area(@RequestBody List<PhysioThirteenArea> areas){
physioThirteenAreaMapper.insert(areas);
return ResultData.success();
}
public static void main(String[] args) {
String s = "1.减少膝关节扭转发力、深度负重深蹲,减少半月板挤压扭转,适度减负休养。2.强化膝关节周边大腿肌群,依靠肌肉力量分担关节内部压力。3.控制体重,降低膝关节整体负重,运动循序渐进,不突然加大运动量。4.膝关节整体做好保暖,寒凉会加重关节间隙酸胀感受。5.养护以膝关节周边肌群疏导为主,禁止直接大力按压关节缝隙。";
List<String> num = Lists.newArrayList("2","3","4","5");
StringBuilder sb = new StringBuilder(s);
for (String n : num) {
int i = sb.indexOf(n);
sb.insert(i,"\\n");
}
System.out.println(sb.toString());
}
}
@@ -0,0 +1,67 @@
package cn.unizk.controller;
import cn.unizk.entity.po.MobileAppUser;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.exception.BusinessException;
import cn.unizk.exception.ServiceException;
import cn.unizk.pay.dto.BasePayResponse;
import cn.unizk.pay.dto.CoursePayParam;
import cn.unizk.pay.dto.HGJPayParam;
import cn.unizk.pay.dto.PayBaseRequest;
import cn.unizk.utils.HttpUtil;
import cn.unizk.utils.JkSecurityUtil;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
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.RestController;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* @Author hao
* @Date 2026/7/27 10:40
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/wechat")
@Slf4j
public class WechatPayController {
@Value("${payIp}")
private String payIp;
@PostMapping("/pay")
public ResultData pay(HttpServletRequest request,@RequestBody PayBaseRequest param){
if (StringUtils.isBlank(param.getOpenid())){
throw new ServiceException("openid呢???");
}
param.setStep(2);
// String ip = "http://192.168.0.6:8083";
String url = payIp + "/mobileApp/hf/pay";
log.info("url===={}",url);
Map<String,String> headers = new HashMap<>();
headers.put("mobile-t",param.getAppToken());
// PayBaseRequest requestType = createRequestType(param);
JSONObject from = JSONObject.from(param);
from.put("sceneCode",param.getSceneCode());
String s = HttpUtil.doPostJson(url, headers, from);
Gson gson = new Gson();
ResultData data = gson.fromJson(s, ResultData.class);
if (data == null || data.getCode() != 200){
throw new ServiceException("微信支付下单失败");
}
return data;
}
}
@@ -0,0 +1,279 @@
package cn.unizk.controller;
import cn.unizk.config.JkProperties;
import cn.unizk.constant.WxConstant;
import cn.unizk.entity.po.*;
import cn.unizk.entity.vo.MobileAppUserVO;
import cn.unizk.entity.vo.OneClickVO;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.exception.ServiceException;
import cn.unizk.mapper.BusinessChannelMapper;
import cn.unizk.mapper.MobileAppUserMapper;
import cn.unizk.mapper.YnzkSystemDeviceMapper;
import cn.unizk.service.*;
import cn.unizk.utils.*;
import cn.unizk.wxPay.service.WxPayOrderService;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* @Author hao
* @Date 2025/7/9 16:25
* @Version 1.0
**/
@RestController
@RequestMapping("/applet")
@Slf4j
public class WxAppletLoginController {
@Resource
private WxAppletLoginService appletLoginService;
@Resource
private WxAuthService wxAuthService;
@Resource
private MobileAppUserMapper mobileAppUserMapper;
@Value("${spring.profiles.active}")
private String active;
@Resource
private JkProperties jkProperties;
@Autowired
@Qualifier("kryoRedisTemplate")
private RedisTemplate<String, Object> redisTemplate;
/* @Resource
private WxPayOrderService wxPayOrderService;*/
@Resource
private DeviceService deviceService;
@Resource
private YnzkSystemDeviceMapper ynzkSystemDeviceMapper;
@Resource
private BusinessChannelMapper businessChannelMapper;
@Resource
private SysDictService dictService;
@GetMapping("/login")
public JSONObject login(@RequestParam("code") String code) {
return appletLoginService.appletLogin(code);
}
@PostMapping("/editWxUser")
public int editUser(@RequestBody MobileAppUser wxUser) {
MobileAppUser user = new MobileAppUser();
user.setNickname(wxUser.getNickname());
user.setAvatar(wxUser.getAvatar());
LambdaUpdateWrapper<MobileAppUser> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(MobileAppUser::getId, wxUser.getId());
return mobileAppUserMapper.update(user, wrapper);
}
@PostMapping("/sendPhoneCode")
public ResultData sendPhoneCode(@RequestParam("phoneNumber") String phoneNumber) {
if ("prod".equals(active)){
String code = RandomStringUtils.randomNumeric(6);
log.info("手机验证码:{}",code);
try {
String s = SendSmsUtil.sendPhoneCode(phoneNumber, code);
JSONObject res = JSONObject.parseObject(s);
Integer statusCode = res.getInteger("statusCode");
if (statusCode != null && statusCode == 200) {
redisTemplate.opsForValue().set(phoneNumber, code, 300, TimeUnit.SECONDS);
return ResultData.success();
}
return ResultData.failure(500, "验证码发送失败");
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
throw new ServiceException("验证码发送失败");
}
}else {
redisTemplate.opsForValue().set(phoneNumber, "888888", 300, TimeUnit.SECONDS);
return ResultData.success();
}
}
/* @PostMapping("/jkLogin")
public MobileAppUserVO JKLogin(@RequestParam("openid") String openid) {
MobileAppUser wxUser = mobileAppUserMapper.selectOne(new LambdaQueryWrapper<MobileAppUser>().eq(MobileAppUser::getOpenid, openid));
if (Objects.isNull(wxUser)) {
throw new ServiceException("登录失败!");
}
MobileAppUserVO wxUserVO = new MobileAppUserVO();
BeanUtils.copyProperties(wxUser, wxUserVO);
String token = JwtUtil.generateToken(wxUser.getId().toString());
redisTemplate.opsForValue().set(WxConstant.CACHE_TOKEN_KEY + wxUser.getId(), wxUser, jkProperties.getTokenExpireTime(), TimeUnit.MINUTES);
wxUserVO.setToken(token);
return wxUserVO;
}*/
@PostMapping("/loginByPhone")
public MobileAppUserVO bindPhoneNumber(@RequestParam("phoneNumber") String phoneNumber, @RequestParam("code") String code,
@RequestParam("openid") String openid) {
if ("prod".equals(active)){
validatePhoneCode(phoneNumber, code);
}
MobileAppUser appUser = mobileAppUserMapper.selectOne(new LambdaQueryWrapper<MobileAppUser>().eq(MobileAppUser::getPhoneNumber, phoneNumber));
if (Objects.isNull(appUser)){
appUser = new MobileAppUser();
appUser.setOpenid(openid);
appUser.setPhoneNumber(phoneNumber);
mobileAppUserMapper.insert(appUser);
}else {
appUser.setOpenid(openid);
mobileAppUserMapper.updateById(appUser);
}
String token = JwtUtil.generateToken(phoneNumber);
MobileAppUserVO mobileAppUserVO = new MobileAppUserVO();
BeanUtils.copyProperties(appUser, mobileAppUserVO);
mobileAppUserVO.setToken(token);
redisTemplate.opsForValue().set(WxConstant.CACHE_TOKEN_KEY + phoneNumber, appUser, jkProperties.getTokenExpireTime(), TimeUnit.DAYS);
return mobileAppUserVO;
}
@PostMapping("/oneClickLogin")
public MobileAppUser oneClick(@RequestBody OneClickVO vo){
String accessToken = wxAuthService.getAccessToken();
String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken;
JSONObject jsonObject = JSONObject.from(vo);
try {
String s = HttpUtil.doPostJson(url, null, jsonObject);
log.info("一键登录接口返回:{}",s);
JSONObject res = JSONObject.parseObject(s);
Integer errcode = res.getInteger("errcode");
if(errcode != 0 && errcode != 42001){
throw new ServiceException("登录失败!请稍后重试");
}
if (errcode == 42001){
redisTemplate.delete(WxConstant.WX_ACCESS_TOKEN);
accessToken = wxAuthService.getAccessToken();
url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken;
String msg = HttpUtil.doPostJson(url, null, jsonObject);
res = JSONObject.parseObject(msg);
Integer code = res.getInteger("errcode");
if (code != 0){
throw new ServiceException("登录失败!请稍后重试");
}
}
JSONObject phone_info = res.getJSONObject("phone_info");
String phoneNumber = phone_info.getString("phoneNumber");
log.info("获取到的用户手机号:{}",phoneNumber);
MobileAppUser user = mobileAppUserMapper.selectOne(new LambdaQueryWrapper<MobileAppUser>().eq(MobileAppUser::getPhoneNumber, phoneNumber));
if (Objects.isNull(user)){
user = new MobileAppUser();
user.setOpenid(vo.getOpenid());
user.setPhoneNumber(phoneNumber);
user.setCreateTime(LocalDateTime.now());
mobileAppUserMapper.insert(user);
}else {
user.setOpenid(vo.getOpenid());
mobileAppUserMapper.updateById(user);
}
String token = JwtUtil.generateToken(phoneNumber);
MobileAppUserVO mobileAppUserVO = new MobileAppUserVO();
BeanUtils.copyProperties(user, mobileAppUserVO);
mobileAppUserVO.setToken(token);
redisTemplate.opsForValue().set(WxConstant.CACHE_TOKEN_KEY + phoneNumber, user, jkProperties.getTokenExpireTime(), TimeUnit.DAYS);
return mobileAppUserVO;
}catch (Exception e){
e.printStackTrace();
throw new ServiceException("登录失败!请稍后重试");
}
}
@GetMapping("/getAppletQrCode")
public void getAppletQrCode(HttpServletResponse response){
String accessToken = wxAuthService.getAccessToken();
String url = String.format("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s",accessToken);
log.info("url={}",url);
JSONObject jsonBody = JSONObject.of();
jsonBody.put("scene","apple=1");
jsonBody.put("check_path",false);
jsonBody.put("env_version","trial");
byte[] bytes = HttpUtil.postRtnBytes(url, null, jsonBody);
if (bytes == null || bytes.length == 0){
ServletUtils.renderString(response,"qrcode res is empty");
return;
}
// response.setContentType("image/jpeg");
response.setContentLength(bytes.length);
try (OutputStream os = response.getOutputStream()) {
os.write(bytes);
os.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
@GetMapping("/logout")
public ResultData logout(HttpServletRequest request){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)){
return ResultData.success();
}
redisTemplate.delete(WxConstant.CACHE_TOKEN_KEY + currentUser.getPhoneNumber());
return ResultData.success();
}
@GetMapping("/getCurrentUser")
public MobileAppUser getCurrentUser(HttpServletRequest request) {
return JkSecurityUtil.getCurrentUser(request);
}
private void validatePhoneCode(String phone, String code) {
String cacheCode = (String) redisTemplate.opsForValue().get(phone);
if (StringUtils.isBlank(cacheCode) || !cacheCode.equals(code)) {
throw new ServiceException("验证码错误");
}
redisTemplate.delete(phone);
}
// @GetMapping("/test")
public boolean test() {
List<YnzkSystemDevice> ynzkSystemDevices = ynzkSystemDeviceMapper.selectList(null);
List<Device> deviceList = new ArrayList<>();
ynzkSystemDevices.forEach(s -> {
Device device = new Device();
device.setMuid(s.getMuid());
device.setDeviceId(s.getSerial());
deviceList.add(device);
});
return deviceService.saveBatch(deviceList);
}
@GetMapping("/test/one")
public String testOne(SysDict dict){
if (Objects.isNull(dict) || dict.getDictCode() == null){
return "fail";
}
boolean b = dictService.updateById(dict);
return b ? "success" : "fail";
}
@GetMapping("/modifyDict")
public void modifyDict(SysDict dict){
dictService.updateById(dict);
}
}
@@ -0,0 +1,32 @@
package cn.unizk.controller;
import cn.unizk.entity.po.Device;
import cn.unizk.entity.vo.UniPage;
import cn.unizk.service.DeviceService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* @Author hao
* @Date 2025/8/30 16:31
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/device")
public class WxDeviceController {
@Resource
private DeviceService deviceService;
@GetMapping("/list")
public Page<Device> list(UniPage uniPage, @RequestParam("storeId") Integer storeId){
Page<Device> page = new Page<>();
page.setCurrent(uniPage.getCurrentPage());
page.setSize(uniPage.getPageSize());
return deviceService.page(page,new LambdaQueryWrapper<Device>().eq(Device::getStatus,"1").eq(Device::getStoreId,storeId));
}
}
@@ -0,0 +1,245 @@
package cn.unizk.controller;
import cn.unizk.constant.WxConstant;
import cn.unizk.entity.po.*;
import cn.unizk.entity.vo.*;
import cn.unizk.enums.OrderAfterSaleStatus;
import cn.unizk.enums.OrderStatus;
import cn.unizk.exception.ServiceException;
import cn.unizk.mapper.*;
import cn.unizk.service.*;
import cn.unizk.utils.BeanCopyUtils;
import cn.unizk.utils.JkSecurityUtil;
import cn.unizk.utils.JwtUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;
/**
* @Author hao
* @Date 2025/8/30 11:36
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/order")
public class WxOrderController {
@Autowired
@Qualifier("kryoRedisTemplate")
private RedisTemplate<String, Object> redisTemplate;
@Resource
private MobileOrderMapper mobileOrderMapper;
@Resource
private ComboService comboService;
@Resource
private JkProductService productService;
@Resource
private JkStoreService storeService;
@Resource
private MobileAppUserMapper mobileAppUserMapper;
@Resource
private JkPhysioService physioService;
@Resource
private MobileOrderItemMapper mobileOrderItemMapper;
@Resource
private OrderReviewMapper reviewMapper;
@Resource
private CourseMapper courseMapper;
@PostMapping("/deleteInvalidOrder")
public ResultData deleteInvalidOrder(@RequestParam("orderId") String orderId, HttpServletRequest request) {
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request.getHeader(WxConstant.APPLET_LOGIN_SIGN));
if (Objects.isNull(currentUser)) {
throw new ServiceException(401, "无权访问");
}
MobileOrder one = mobileOrderMapper.selectOne(new LambdaQueryWrapper<MobileOrder>()
.eq(MobileOrder::getOrderId, orderId)
.eq(MobileOrder::getMobileUserId, currentUser.getId()));
if (Objects.isNull(one)) {
throw new ServiceException("您无权限删除");
}
mobileOrderMapper.deleteById(orderId);
return ResultData.success();
}
@GetMapping("/getPersonOrders")
public Page<MobileOrderVO> getPersonOrders(HttpServletRequest request, UniPage uniPage, @RequestParam(value = "orderStatus", required = false) String orderStatus) {
boolean afterSale = false;
if ("request_refund,refunded".equals(orderStatus)) {
orderStatus = "";
afterSale = true;
}
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)) {
return null;
}
List<String> orderStatusList = null;
if (StringUtils.isNotBlank(orderStatus) && orderStatus.contains(",")) {
String[] split = orderStatus.split(",");
orderStatusList = Arrays.asList(split);
} else if (StringUtils.isNotBlank(orderStatus)) {
orderStatusList = new ArrayList<>();
orderStatusList.add(orderStatus);
}
Page<MobileOrder> page = new Page<>();
page.setSize(uniPage.getPageSize());
page.setCurrent(uniPage.getCurrentPage());
// ArrayList<String> strings = Lists.newArrayList("unpaid", "not_shipped", "shipped", "to_be_evaluate");
Page<MobileOrder> orderPage = mobileOrderMapper.selectPage(page, new LambdaQueryWrapper<MobileOrder>()
.eq(MobileOrder::getMobileUserId, currentUser.getId())
.in(CollectionUtils.isNotEmpty(orderStatusList), MobileOrder::getStatus, orderStatusList)
.eq(!afterSale && StringUtils.isNotBlank(orderStatus), MobileOrder::getAfterSaleStatus, 0)
.ne(afterSale, MobileOrder::getAfterSaleStatus, 0)
.orderByDesc(MobileOrder::getCreateTime)
/*.in(WxOrder::getStatus,strings)*/);
List<MobileOrder> records = orderPage.getRecords();
/*List<WxOrder> collect = records.stream().filter(o -> {
return strings.contains(o.getStatus());
}).collect(Collectors.toList());*/
List<MobileOrderVO> vos = new ArrayList<>();
for (MobileOrder order : records) {
MobileOrderVO vo = new MobileOrderVO();
BeanUtils.copyProperties(order, vo);
/*if (order.getAfterSaleStatus() != 0){
vo.setStatus(OrderAfterSaleStatus.getValueByKey(order.getAfterSaleStatus()));
}*/
List<MobileOrderItem> wxOrderItems = mobileOrderItemMapper.selectList(new LambdaQueryWrapper<MobileOrderItem>().eq(MobileOrderItem::getMobileOrderId, order.getOrderId()));
Collection<MobileOrderItemVO> wxOrderItemVOS = BeanCopyUtils.copyArrays(wxOrderItems, MobileOrderItemVO::new);
if (CollectionUtils.isEmpty(wxOrderItemVOS)) {
vos.add(vo);
continue;
}
for (MobileOrderItemVO wxOrderItemVO : wxOrderItemVOS) {
if (wxOrderItemVO.getProductId() != null) {
JkProduct product = productService.getById(wxOrderItemVO.getProductId());
if (product != null){
wxOrderItemVO.setProductName(product.getProductName());
wxOrderItemVO.setImage(product.getProductImage());
}
} else if (wxOrderItemVO.getComboId() != null) {
Combo combo = comboService.getById(wxOrderItemVO.getComboId());
if (combo != null){
wxOrderItemVO.setProductName(combo.getComboName());
wxOrderItemVO.setImage(combo.getImage());
}
}
}
vo.setOrderItemVOS((List<MobileOrderItemVO>) wxOrderItemVOS);
vos.add(vo);
}
Page<MobileOrderVO> voPage = new Page<>();
voPage.setTotal(orderPage.getTotal());
voPage.setSize(orderPage.getSize());
voPage.setCurrent(orderPage.getCurrent());
voPage.setRecords(vos);
return voPage;
}
@GetMapping("/getSelfOneOrder")
public MobileOrderDetailVO getSelfOneOrder(@RequestParam("orderId") String orderId, HttpServletRequest request) {
MobileOrder order = mobileOrderMapper.selectById(orderId);
if (Objects.isNull(order)) {
return new MobileOrderDetailVO();
}
MobileOrderDetailVO vo = new MobileOrderDetailVO();
BeanUtils.copyProperties(order, vo);
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser) || !currentUser.getId().equals(order.getMobileUserId())) {
throw new ServiceException(401, "无权限访问");
}
if (order.getStoreId() != null) {
JkStore jkStore = storeService.getById(order.getStoreId());
vo.setJkStore(jkStore);
}
if (order.getPhysioId() != null) {
JkPhysio jkPhysio = physioService.getById(order.getPhysioId());
vo.setJkPhysio(jkPhysio);
}
if (order.getProductId() != null){
JkProduct jkProduct = productService.getById(order.getProductId());
vo.setProductName(jkProduct.getProductName());
}
if (order.getComboId() != null){
Combo combo = comboService.getById(order.getComboId());
vo.setProductName(combo.getComboName());
}
if (order.getCourseId() != null){
Course course = courseMapper.selectById(order.getCourseId());
vo.setProductName(course.getTitle());
}
List<MobileOrderItem> wxOrderItems = mobileOrderItemMapper.selectList(new LambdaQueryWrapper<MobileOrderItem>().eq(MobileOrderItem::getMobileOrderId, order.getOrderId()));
if (CollectionUtils.isNotEmpty(wxOrderItems)) {
Collection<MobileOrderItemVO> wxOrderItemVOS = BeanCopyUtils.copyArrays(wxOrderItems, MobileOrderItemVO::new);
for (MobileOrderItemVO itemVO : wxOrderItemVOS) {
if (itemVO.getProductId() != null) {
JkProduct product = productService.getById(itemVO.getProductId());
itemVO.setProductName(product.getProductName());
itemVO.setImage(product.getProductImage());
} else if (itemVO.getComboId() != null) {
Combo combo = comboService.getById(itemVO.getComboId());
itemVO.setImage(combo.getImage());
itemVO.setProductName(combo.getComboName());
}
}
vo.setOrderItemVOS((List<MobileOrderItemVO>) wxOrderItemVOS);
}
MobileAppUser user = mobileAppUserMapper.selectOne(new LambdaQueryWrapper<MobileAppUser>().eq(MobileAppUser::getId, order.getMobileUserId()));
vo.setMobileAppUser(user);
return vo;
}
@GetMapping("/getSortSum")
public JSONObject getSum(HttpServletRequest request) {
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
JSONObject of = JSONObject.of();
List<MobileOrder> list = mobileOrderMapper.selectList(new LambdaQueryWrapper<MobileOrder>().eq(MobileOrder::getMobileUserId, currentUser.getId()));
List<MobileOrder> noAfterSales = list.stream().filter(o -> o.getAfterSaleStatus().equals(0)).collect(Collectors.toList());
long unpaid = noAfterSales.stream().filter(o -> OrderStatus.unpaid.name().equals(o.getStatus())).count();
of.put("待付款", unpaid);
long daifahuo = noAfterSales.stream().filter(o -> OrderStatus.not_shipped.name().equals(o.getStatus())).count();
of.put("待发货", daifahuo);
long daishouhuo = noAfterSales.stream().filter(o -> (OrderStatus.shipped.name().equals(o.getStatus()) || OrderStatus.unverified.name().equals(o.getStatus()))).count();
of.put("待收货/使用", daishouhuo);
long daipingjia = noAfterSales.stream().filter(o -> (OrderStatus.received.name().equals(o.getStatus()) || OrderStatus.verified.name().equals(o.getStatus()))).count();
of.put("待评价", daipingjia);
ArrayList<Integer> integers = Lists.newArrayList(0, 5, 6, 7);
long tuikuan = list.stream().filter(o -> !integers.contains(o.getAfterSaleStatus())).count();
of.put("退款", tuikuan);
return of;
}
@PostMapping("/comment")
@Transactional(rollbackFor = Exception.class)
public ResultData comment(@Validated @RequestBody OrderReview review){
MobileOrder wxOrder = mobileOrderMapper.selectById(review.getOrderId());
if (Objects.isNull(wxOrder)){
throw new ServiceException("无此订单");
}
MobileAppUser wxUser = mobileAppUserMapper.selectOne(new LambdaQueryWrapper<MobileAppUser>().eq(MobileAppUser::getId, wxOrder.getMobileUserId()));
if (Objects.isNull(wxUser)){
throw new ServiceException("数据出现异常");
}
wxUser.setPoints(wxUser.getPoints() + 1);
mobileAppUserMapper.updateById(wxUser);
reviewMapper.insert(review);
wxOrder.setStatus(OrderStatus.commented.name());
mobileOrderMapper.updateById(wxOrder);
return ResultData.success();
}
}
@@ -0,0 +1,54 @@
package cn.unizk.controller;
import cn.unizk.config.WxProperties;
import cn.unizk.constant.WxConstant;
import cn.unizk.entity.po.MobileAppUser;
import cn.unizk.entity.vo.MobileOrderVO;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.exception.ServiceException;
import cn.unizk.service.PayService;
import cn.unizk.utils.JkSecurityUtil;
import cn.unizk.wxPay.vo.WxPlaceOrderResVO;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
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.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
/**
* @Author hao
* @Date 2025/8/28 18:17
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/wxPay")
@Slf4j
public class WxPayController {
@Resource(name = "wx")
private PayService payService;
@Resource
private WxProperties wxProperties;
@PostMapping("/placeOrder")
public WxPlaceOrderResVO placeOrder(@RequestBody MobileOrderVO orderVO, HttpServletRequest request){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request.getHeader(WxConstant.APPLET_LOGIN_SIGN));
if (Objects.isNull(currentUser) || !currentUser.getId().equals(orderVO.getMobileUserId())){
throw new ServiceException(403,"无权访问该资源");
}
return payService.wxPlaceOrder(orderVO);
}
@PostMapping("/paySuccessCallBack")
public ResultData callback(@RequestBody JSONObject body, HttpServletRequest request){
payService.wxPaySuccessHandler(request,body);
return ResultData.success();
}
}
@@ -0,0 +1,348 @@
package cn.unizk.controller;
import cn.unizk.config.JkProperties;
import cn.unizk.constant.WxConstant;
import cn.unizk.entity.po.*;
import cn.unizk.entity.vo.ReportReqVO;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.enums.OrderStatus;
import cn.unizk.exception.ServiceException;
import cn.unizk.mapper.*;
import cn.unizk.service.JkPhysioService;
import cn.unizk.service.UserAddressService;
import cn.unizk.service.WxUserService;
import cn.unizk.utils.JkSecurityUtil;
import cn.unizk.utils.QRcodeUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Clock;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* @Author hao
* @Date 2025/10/13 18:38
* @Version 1.0
**/
@RestController
@RequestMapping("/applet/user")
@Slf4j
public class WxUserController {
@Resource
private MobileAppUserMapper mobileAppUserMapper;
@Resource
private MobileUserAddressMapper mobileUserAddressMapper;
@Resource
private MobileOrderMapper mobileOrderMapper;
@Resource
private SysDictMapper sysDictMapper;
@Resource
private GrfPjMapper grfPjMapper;
@Resource
private MobileAppUserAccountMapper mobileAppUserAccountMapper;
@Resource
private RechargePackageMapper rechargePackageMapper;
@Resource
private JkPhysioService jkPhysioService;
@Autowired
@Qualifier("kryoRedisTemplate")
private RedisTemplate<String, Object> redisTemplate;
@Resource
private JkProperties jkProperties;
@Resource
private UserFreeTrialInfoMapper userFreeTrialInfoMapper;
@GetMapping("/freeTrialQRcode")
public void freeTrial(HttpServletResponse response,HttpServletRequest request){
log.info("获取二维码");
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)){
throw new ServiceException(401,"登录已过期");
}
String url = "hello,userId=" + currentUser.getId();
QRcodeUtil.generateQrcode(response,url,null);
}
@GetMapping("/checkFreeTrial")
public ResultData<JSONObject> checkFree(HttpServletRequest request){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
UserFreeTrialInfo userFreeTrialInfo = userFreeTrialInfoMapper.selectOne(new LambdaQueryWrapper<UserFreeTrialInfo>()
.eq(UserFreeTrialInfo::getUserId, currentUser.getId()));
MobileAppUser user = mobileAppUserMapper.selectById(currentUser.getId());
ResultData<JSONObject> resultData = new ResultData<>();
JSONObject jsonObject = new JSONObject();
resultData.setCode(200);
if (Objects.isNull(userFreeTrialInfo)){
jsonObject.put("flag",0);
}else {
jsonObject.put("flag",1);
}
jsonObject.put("firstTrial",user.getFreeTrial());
resultData.setData(jsonObject);
return resultData;
}
@PostMapping("/saveFreeTrialInfo")
public ResultData saveFreeTrialInfo(HttpServletRequest request,@RequestBody UserFreeTrialInfo info){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
UserFreeTrialInfo userFreeTrialInfo = userFreeTrialInfoMapper.selectOne(new LambdaQueryWrapper<UserFreeTrialInfo>()
.eq(UserFreeTrialInfo::getUserId, currentUser.getId()));
if (Objects.nonNull(userFreeTrialInfo)){
throw new ServiceException("用户已存在数据");
}
info.setUserId(currentUser.getId());
info.setCreateTime(LocalDateTime.now());
userFreeTrialInfoMapper.insert(info);
return ResultData.success();
}
@PostMapping("/editUserInfo")
public ResultData editUserInfo(@RequestBody MobileAppUser user){
mobileAppUserMapper.updateById(user);
return ResultData.success();
}
@GetMapping("/getUserIdType")
public ResultData<Integer> getUserIdType(HttpServletRequest request){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)){
throw new ServiceException(401,"登录已过期");
}
JkPhysio one = jkPhysioService.getOne(new LambdaQueryWrapper<JkPhysio>().eq(JkPhysio::getPhysioTel, currentUser.getPhoneNumber()));
ResultData<Integer> data = new ResultData<>();
data.setCode(200);
if (Objects.isNull(one)){
data.setData(0);
}else {
data.setData(1);
}
return data;
}
@PostMapping("/switchIdType")
public ResultData switchIdType(HttpServletRequest request,@RequestParam("type") String type){
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)){
throw new ServiceException(401,"登录已过期");
}
if (!type.equals("C") && !type.equals("P")){
throw new ServiceException("未知的身份类型");
}
currentUser.setUserType(type);
mobileAppUserMapper.updateById(currentUser);
redisTemplate.delete(WxConstant.CACHE_TOKEN_KEY + currentUser.getPhoneNumber());
redisTemplate.opsForValue().set(WxConstant.CACHE_TOKEN_KEY + currentUser.getPhoneNumber(), currentUser, jkProperties.getTokenExpireTime(), TimeUnit.DAYS);
return ResultData.success();
}
@PostMapping("/rememberDia")
public ResultData<Integer> rememberDia(HttpServletRequest request, @Validated @RequestBody ReportReqVO vo) {
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)) {
throw new ServiceException(401, "登录已过期");
}
MobileAppUser user = new MobileAppUser();
user.setId(currentUser.getId());
user.setSymptom(vo.getReport());
user.setAge(vo.getAge());
user.setHeight(vo.getHeight());
user.setWeight(vo.getWeight());
user.setGender(vo.getGender());
// user.setTemplateId(vo.getTemplateId());
user.setPainNumber(vo.getPainNumber());
mobileAppUserMapper.updateById(user);
List<String> integers = Lists.newArrayList("1", "2", "4", "5", "7", "8", "10", "11", "12", "13");
boolean b = false;
int t = 2;
if (vo.getPainNumber().contains(",")) {
List<String> ids = Arrays.asList(vo.getPainNumber().split(","));
for (String id : ids) {
if (!integers.contains(id)) {
b = true;
break;
}
}
if (!b) {
if ("000".equals(vo.getCode1()) && "000".equals(vo.getCode2())) {
t = 1;
}
}
} else {
if (!integers.contains(vo.getPainNumber())) {
b = true;
} else {
if ("000".equals(vo.getCode1()) && "000".equals(vo.getCode2())) {
t = 1;
}
}
}
ResultData<Integer> data = new ResultData<>();
data.setCode(200);
data.setData(t);
return data;
}
@GetMapping("/addresses")
public List<MobileUserAddress> addresses(HttpServletRequest request) {
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (Objects.isNull(currentUser)) {
throw new ServiceException(401, "登录已过期");
}
return mobileUserAddressMapper.selectList(new LambdaQueryWrapper<MobileUserAddress>()
.eq(MobileUserAddress::getMobileUserId, currentUser.getId()));
}
@PostMapping("/addOrUpdateAddress")
public int addOrUpdateAddress(@RequestBody MobileUserAddress address, HttpServletRequest request) {
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (currentUser == null) {
throw new ServiceException(401, "登陆已过期");
}
if (!currentUser.getId().equals(address.getMobileUserId())) {
throw new ServiceException(403, "无权访问");
}
List<MobileUserAddress> wxUserAddresses = mobileUserAddressMapper.selectList(null);
if (CollectionUtils.isEmpty(wxUserAddresses)) {
address.setDefaultAddr(0);
}
if (address.getAddressId() == null && address.getDefaultAddr() == 0) {
MobileUserAddress one = mobileUserAddressMapper.selectOne(new LambdaQueryWrapper<MobileUserAddress>().eq(MobileUserAddress::getDefaultAddr, 0));
if (Objects.nonNull(one)) {
one.setDefaultAddr(1);
mobileUserAddressMapper.updateById(one);
}
}
if (address.getAddressId() != null && address.getDefaultAddr() == 0) {
MobileUserAddress userAddress = mobileUserAddressMapper.selectById(address.getAddressId());
if (Objects.isNull(userAddress)) {
throw new ServiceException("no this address");
}
if (userAddress.getDefaultAddr() != 0) {
MobileUserAddress one = mobileUserAddressMapper.selectOne(new LambdaQueryWrapper<MobileUserAddress>().eq(MobileUserAddress::getDefaultAddr, 0));
if (Objects.nonNull(one)) {
one.setDefaultAddr(1);
mobileUserAddressMapper.updateById(one);
}
}
}
if (address.getAddressId() == null) {
address.setCreateTime(LocalDateTime.now(Clock.systemDefaultZone()));
} else {
address.setUpdateTime(LocalDateTime.now(Clock.systemDefaultZone()));
}
mobileUserAddressMapper.insertOrUpdate(address);
return address.getAddressId();
}
@PostMapping("/deleteAddr")
public int deleteAddr(@RequestParam("addressId") Integer addressId, HttpServletRequest request) {
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (currentUser == null) {
throw new ServiceException(401, "登陆已过期");
}
MobileUserAddress userAddress = mobileUserAddressMapper.selectById(addressId);
if (Objects.isNull(userAddress)) {
throw new ServiceException("地址不存在");
}
if (!currentUser.getId().equals(userAddress.getMobileUserId())) {
throw new ServiceException(403, "无权访问");
}
return mobileUserAddressMapper.deleteById(addressId);
}
@PostMapping("/applyRefund")
public int applyRefund(@RequestBody MobileOrder order, HttpServletRequest request) {
MobileOrder mobileOrder = mobileOrderMapper.selectById(order.getOrderId());
if (Objects.isNull(mobileOrder)) {
throw new ServiceException("查无此单");
}
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (currentUser == null || !currentUser.getId().equals(mobileOrder.getMobileUserId())) {
throw new ServiceException("无权访问");
}
// wxOrder.setStatus(OrderStatus.request_refund.name());
mobileOrder.setApplyRefundTime(LocalDateTime.now());
mobileOrder.setRefundReason(order.getRefundReason());
mobileOrder.setRefundTrackingNumber(order.getRefundTrackingNumber());
mobileOrder.setAfterSaleStatus(order.getAfterSaleStatus());
return mobileOrderMapper.updateById(mobileOrder);
}
@PostMapping("/refundReason")
public int refundReason(@RequestParam("orderId") String orderId, @RequestParam("reason") String reason, HttpServletRequest request) {
MobileOrder mobileOrder = mobileOrderMapper.selectById(orderId);
if (Objects.isNull(mobileOrder)) {
throw new ServiceException("无此订单");
}
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
if (currentUser == null || !currentUser.getId().equals(mobileOrder.getMobileUserId())) {
throw new ServiceException("无权访问");
}
mobileOrder.setRefundReason(reason);
return mobileOrderMapper.updateById(mobileOrder);
}
@GetMapping("/getGrfScanPrice")
public ResultData getGrfScanPrice(HttpServletRequest request, @RequestParam(value = "id", required = false) Integer id) {
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
List<MobileOrder> wxOrders = mobileOrderMapper.selectList(new LambdaQueryWrapper<MobileOrder>()
.eq(MobileOrder::getMobileUserId, currentUser.getId())
.eq(MobileOrder::getStatus, OrderStatus.paid.name())
.eq(MobileOrder::getOrderType, 1));
ResultData<String> data = new ResultData<>();
if (CollectionUtils.isEmpty(wxOrders)) {
data.setCode(200);
SysDict sysDict = sysDictMapper.selectOne(new LambdaQueryWrapper<SysDict>().eq(SysDict::getDictType, "grf"));
data.setData(Objects.nonNull(sysDict) ? sysDict.getDictValue() : "99");
return data;
} else {
if (id == null) {
throw new ServiceException("您不是首次体验用户!");
}
GrfPj grfPj = grfPjMapper.selectById(id);
data.setCode(200);
data.setData(grfPj.getPrice().toString());
return data;
}
}
@GetMapping("/getGrfPrice")
public List<GrfPj> list() {
return grfPjMapper.selectList(null);
}
@GetMapping("/getAccountBalance")
public MobileAppUserAccount account(HttpServletRequest request) {
MobileAppUser currentUser = JkSecurityUtil.getCurrentUser(request);
MobileAppUserAccount account = mobileAppUserAccountMapper.selectOne(new LambdaQueryWrapper<MobileAppUserAccount>().eq(MobileAppUserAccount::getMobileUserId, currentUser.getId()));
if (account == null) {
return new MobileAppUserAccount();
}
return account;
}
@GetMapping("/getVipRechargePackage")
public List<RechargePackage> rechargePackages() {
return rechargePackageMapper.selectList(null);
}
}
@@ -0,0 +1,154 @@
/*
package cn.unizk.controller;
import cn.unizk.constant.WxConstant;
import cn.unizk.entity.po.WxUser;
import cn.unizk.entity.vo.CallbackResponse;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.exception.ServiceException;
import cn.unizk.mapper.WxOrderMapper;
import cn.unizk.utils.JkSecurityUtil;
import cn.unizk.wxPay.vo.WxOrderVO;
import cn.unizk.wxPay.vo.WxPlaceOrderResVO;
import cn.unizk.xunfupay.dto.AddFzAccountDTO;
import cn.unizk.xunfupay.dto.PaySuccessCallbackDTO;
import cn.unizk.xunfupay.dto.QueryOrderTradeDetailResponse;
import cn.unizk.xunfupay.service.XunFuPayService;
import cn.unizk.xunfupay.util.FileUploadUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Objects;
*/
/**
* @Author hao
* @Date 2025/9/22 16:35
* @Version 1.0
**//*
@RestController
@Slf4j
@RequestMapping("/applet/xunfuPay")
public class XunFuPayController {
@Resource
private XunFuPayService xunFuPayService;
@Resource
private WxOrderMapper orderMapper;
*/
/**
* 绑定微信小程序appid
* @return
*//*
@PostMapping("/bindWxAppid")
public boolean bindWxAppid(){
return xunFuPayService.bindWxAppID();
}
*/
/**
* 开通分账功能
* @return
*//*
@PostMapping("/activateFz")
public boolean activateFz(){
return xunFuPayService.activateFz();
}
*/
/**
* 添加分账接收方账户
* @param dto
* @return
*//*
@PostMapping("/addFzAccount")
public ResultData addFzAccount(@RequestBody AddFzAccountDTO dto,@RequestParam("storeId") Integer storeId){
return xunFuPayService.addFzAccount(dto,storeId);
}
*/
/**
* idCardFront-法人身份证正面(人面),
* idCardBack-法人身份证反面(国徽面),
* personWithIdCard-法人手持身份证,
* bankCard-结算银行卡照片(对私上送),
* license-营业执照,
* openAccountLicenses-开户许可证(对公上送)
* @return url
*//*
@PostMapping("/uploadFile/{fileType}")
public String getUploadToken(@RequestParam("file")MultipartFile file, @PathVariable("fileType") String fileType){
String filename = file.getOriginalFilename();
if (StringUtils.isBlank(filename) || !filename.contains(".")){
throw new ServiceException("文件类型不正确");
}
String name = filename.substring(filename.lastIndexOf("/") + 1);
try {
return FileUploadUtil.autoUploadFile(file.getInputStream(),name,fileType);
} catch (IOException e) {
e.printStackTrace();
return "file upload failure";
}
}
*/
/**
* 分账接收方签约
* @return
* @throws NoSuchAlgorithmException
*//*
@PostMapping("/qianyue")
public String qianyue() throws NoSuchAlgorithmException {
return xunFuPayService.qianyue();
}
*/
/**
* 下单
* @param vo
* @param request
* @return
*//*
@PostMapping("/placeOrder")
public WxPlaceOrderResVO placeOrder(@RequestBody WxOrderVO vo, HttpServletRequest request){
WxUser currentUser = JkSecurityUtil.getCurrentUser(request.getHeader(WxConstant.APPLET_LOGIN_SIGN));
if (Objects.isNull(currentUser) || !currentUser.getOpenid().equals(vo.getOpenid())){
throw new ServiceException(403,"无权访问该资源");
}
return xunFuPayService.xunfuPlaceOrder(vo);
}
@PostMapping("/callback")
public CallbackResponse callback(@RequestBody PaySuccessCallbackDTO dto){
log.info("环迅支付成功回调返回参数:{}",dto.toString());
try {
QueryOrderTradeDetailResponse queryOrderTradeDetailResponse = xunFuPayService.queryOrderTradeDetailResponse(dto.getOrderNo());
String settleAmount = queryOrderTradeDetailResponse.getSettleAmount();
String orderNo = queryOrderTradeDetailResponse.getOrderNo();
boolean b = xunFuPayService.applyFz(orderNo, settleAmount);
log.info("订单{}分账是否成功:{}",orderNo,b);
//修改订单支付状态
orderMapper.updateStatusToPaid(orderNo);
return new CallbackResponse("1");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return new CallbackResponse("1");
}
}
}
*/
@@ -0,0 +1,45 @@
package cn.unizk.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author hao
* @Date 2025/9/13 17:01
* @Version 1.0
**/
@Slf4j
public class JkCorsFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpServletRequest request = (HttpServletRequest) servletRequest;
log.info("进入jkcorsfilter过滤器");
// 允许任何域名(生产环境请换成具体域名)
response.setHeader("Access-Control-Allow-Origin", "http://369.peaktcm.com:9000");
// 允许带自定义请求头
response.setHeader("Access-Control-Allow-Headers", "Content-Type,token");
response.setHeader("Access-Control-Expose-Headers", "Authorization");
// 允许浏览器携带 cookie
response.setHeader("Access-Control-Allow-Credentials", "true");
// 允许的 HTTP 方法
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
// 关键:预检请求直接返回 200,其他正常放行
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
return; // 预检结束
}
filterChain.doFilter(servletRequest, servletResponse);
}
}
@@ -0,0 +1,34 @@
package cn.unizk.huifupay.config.properties;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @Author hao
* @Date 2025/9/28 16:49
* @Version 1.0
**/
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "huifu")
public class HuiFuProperties {
private String huifuId;
private String payApi;
private String refundApi;
private String publicKey;
private String privateKey;
private String notifyUrl;
private String productId;
private String configureAppletCallbackUrl;
}
@@ -0,0 +1,30 @@
package cn.unizk.huifupay.config.properties;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @Author hao
* @Date 2026/3/11 10:43
* @Version 1.0
**/
@ConfigurationProperties(prefix = "huifu-vendor")
@Component
@Getter
@Setter
public class HuiFuVendorProperties {
private String sysId;
private String huifuId;
private String publicKey;
private String privateKey;
private String productId;
private String notifyUrl;
}
@@ -0,0 +1,12 @@
package cn.unizk.huifupay.dto;
import lombok.Data;
/**
* @Author hao
* @Date 2025/9/29 13:35
* @Version 1.0
**/
@Data
public class HuifuBaseResponse {
}
@@ -0,0 +1,18 @@
package cn.unizk.huifupay.dto;
import lombok.Data;
/**
* @Author hao
* @Date 2026/4/30 10:34
* @Version 1.0
**/
@Data
public class HuifuPlaceOrderParamBaseDTO<T> {
private String openid;
private Integer userId;
private String goodsDesc;
private String placeOrderAmount;
private T t;
}
@@ -0,0 +1,291 @@
package cn.unizk.huifupay.service;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.unizk.config.WxProperties;
import cn.unizk.entity.po.JkStore;
import cn.unizk.entity.po.MobileOrder;
import cn.unizk.entity.po.MobileOrderItem;
import cn.unizk.entity.vo.MobileOrderItemVO;
import cn.unizk.entity.vo.MobileOrderVO;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.enums.OrderStatus;
import cn.unizk.exception.ServiceException;
import cn.unizk.huifupay.config.properties.HuiFuProperties;
import cn.unizk.huifupay.utils.HuifuSignUtil;
import cn.unizk.mapper.JkStoreMapper;
import cn.unizk.mapper.MobileOrderItemMapper;
import cn.unizk.mapper.MobileOrderMapper;
import cn.unizk.service.PayService;
import cn.unizk.utils.BeanCopyUtils;
import cn.unizk.utils.HttpUtil;
import cn.unizk.wxPay.service.WxPayOrderService;
import cn.unizk.wxPay.vo.WxPlaceOrderResVO;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.time.Clock;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
/**
* @Author hao
* @Date 2025/9/28 16:28
* @Version 1.0
**/
@Service("huifu")
@Slf4j
public class HuiFuPayService implements PayService {
@Resource
private WxProperties wxProperties;
@Resource
private HuiFuProperties huiFuProperties;
@Resource
private MobileOrderMapper mobileOrderMapper;
/*@Resource
private WxOrderCloseScheduler wxOrderCloseScheduler;*/
@Resource
private WxPayOrderService wxPayOrderService;
@Resource
private MobileOrderItemMapper mobileOrderItemMapper;
@Resource
private JkStoreMapper jkStoreMapper;
/**
* 防重复支付,前端防抖处理加随机uuid传给后端,调下单接口返回成功后变换uuid,
* 后端根据uuid+redission分布式锁+双重检查锁处理
* @param orderVO
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public WxPlaceOrderResVO wxPlaceOrder(MobileOrderVO orderVO) {
// String orderId = IdUtil.getSnowflakeNextIdStr();
String outTradeNo = RandomStringUtils.randomNumeric(30);
List<MobileOrderItemVO> orderItemVOS = orderVO.getOrderItemVOS();
if (CollectionUtils.isEmpty(orderItemVOS)){
throw new ServiceException("参数错误");
}
boolean b = orderItemVOS.stream()
.map(MobileOrderItemVO::getStoreId)
.distinct()
.count() == 1;
Map<String,Object> params = new HashMap<>();
params.put("sys_id",huiFuProperties.getHuifuId());
params.put("product_id","EDUSTD");
JSONObject data = JSONObject.of();
data.put("req_date", DateUtil.format(new Date(),"yyyyMMdd"));
data.put("req_seq_id",outTradeNo);
data.put("huifu_id",huiFuProperties.getHuifuId());
data.put("goods_desc",orderVO.getDescription());
data.put("trade_type","T_MINIAPP");
String sumPrice = new DecimalFormat("0.00").format(orderVO.getTotalPrice());
data.put("trans_amt",sumPrice);
DateTime dateTime = DateUtil.offsetMinute(new Date(), +5);
String yyyyMMddHHmmss = DateUtil.format(dateTime, "yyyyMMddHHmmss");
data.put("time_expire",yyyyMMddHHmmss);
JSONObject wx_data = JSONObject.of();
wx_data.put("sub_appid",wxProperties.getAppid());
wx_data.put("sub_openid",orderVO.getOpenid());
data.put("wx_data",wx_data.toString());
data.put("fee_flag",2);
//分账对象
JSONObject acct_split_bunch = JSONObject.of();
JSONArray array = new JSONArray();
if (b){
JSONObject acct_infos2 = new JSONObject();
JkStore jkStore = jkStoreMapper.selectById(orderVO.getOrderItemVOS().get(0).getStoreId());
if (Objects.isNull(jkStore)){
log.info("门店不存在,门店id:{},不参与分账",orderVO.getOrderItemVOS().get(0).getStoreId());
}else {
if (StringUtils.isBlank(jkStore.getHuifuId()) || jkStore.getProfitSharingPercentage() == null){
throw new ServiceException(String.format("门店:%s的汇付id或分账比为空",jkStore.getStoreName()));
}
acct_infos2.put("huifu_id",jkStore.getHuifuId());
// acct_infos2.put("huifu_id","6666000179598737");
// acct_infos2.put("acct_id","755980768110008");
//分账比例
log.info("子商户分账比例为:{}",jkStore.getProfitSharingPercentage().toString());
acct_infos2.put("percentage_div",jkStore.getProfitSharingPercentage().toString());
array.add(acct_infos2);
JSONObject parent_acct_infos = new JSONObject();
parent_acct_infos.put("huifu_id",huiFuProperties.getHuifuId());
BigDecimal b100 = new BigDecimal("100.00");
BigDecimal surplusPercent = b100.subtract(jkStore.getProfitSharingPercentage());
log.info("主商户分账比例为:{}",surplusPercent);
parent_acct_infos.put("percentage_div",surplusPercent.toString());
array.add(parent_acct_infos);
}
}else {
double percent = 0.00;
for (MobileOrderItemVO itemVO : orderItemVOS) {
JkStore jkStore = jkStoreMapper.selectById(itemVO.getStoreId());
if (Objects.isNull(jkStore)){
throw new ServiceException(String.format("门店不存在,门店ID:%d",orderItemVOS.get(0).getStoreId()));
}
if (StringUtils.isBlank(jkStore.getHuifuId()) || jkStore.getProfitSharingPercentage() == null){
throw new ServiceException(String.format("门店:%s的汇付id或分账比为空",jkStore.getStoreName()));
}
JSONObject acct_infos2 = new JSONObject();
acct_infos2.put("huifu_id",jkStore.getHuifuId());
double singleProductPercent = (itemVO.getPrice().doubleValue() / orderVO.getTotalPrice().doubleValue()) * jkStore.getProfitSharingPercentage().doubleValue();
acct_infos2.put("percentage_div",singleProductPercent);
percent += singleProductPercent;
array.add(acct_infos2);
}
double surplusPercent = 100.00 - percent;
JSONObject parent_acct_infos = new JSONObject();
parent_acct_infos.put("huifu_id",huiFuProperties.getHuifuId());
parent_acct_infos.put("percentage_div",String.valueOf(surplusPercent));
array.add(parent_acct_infos);
}
if (CollectionUtils.isNotEmpty(array)){
acct_split_bunch.put("acct_infos",array);
acct_split_bunch.put("percentage_flag","Y");
data.put("acct_split_bunch",acct_split_bunch.toString());
}
data.put("notify_url",huiFuProperties.getNotifyUrl());
TreeMap treeMap = JSONObject.parseObject(data.toString(), TreeMap.class);
JSONObject signBody = new JSONObject(treeMap);
// String s = Base64.getEncoder().encodeToString(huiFuProperties.getPrivateKey().getBytes(StandardCharsets.UTF_8));
String sign = HuifuSignUtil.sign(signBody.toString(), huiFuProperties.getPrivateKey());
params.put("sign",sign);
params.put("data",data);
log.info("下单请求参数:{}",params);
String res = HttpUtil.doPostJson(huiFuProperties.getPayApi(), null, new JSONObject(params));
log.info("汇付下单返回:{}",res);
JSONObject response = JSONObject.parseObject(res);
if (response == null){
throw new ServiceException("下单失败");
}
JSONObject responseData = response.getJSONObject("data");
String resp_code = responseData.getString("resp_code");
String hf_seq_id = responseData.getString("hf_seq_id");
if ("00000100".equals(resp_code)){
log.info("订单:{}下单成功",outTradeNo);
//生成订单
if (b){
String orderId = IdUtil.getSnowflakeNextIdStr();
MobileOrder wxOrder = new MobileOrder();
BeanUtils.copyProperties(orderVO, wxOrder);
wxOrder.setOutTradeNo(outTradeNo);
wxOrder.setTotalPrice(orderVO.getTotalPrice());
wxOrder.setStatus(OrderStatus.unpaid.name());
wxOrder.setOrderId(orderId);
wxOrder.setStoreId(orderItemVOS.get(0).getStoreId());
wxOrder.setDeviceId(orderVO.getDeviceId());
wxOrder.setPhysioId(orderVO.getPhysioId());
wxOrder.setComboId(orderItemVOS.get(0).getComboId());
/*wxOrder.setProductId(orderVO.getProductId());
wxOrder.setComboId(orderVO.getComboId());*/
wxOrder.setCreateTime(LocalDateTime.now(Clock.systemDefaultZone()));
wxOrder.setRecipientPhone(orderVO.getRecipientPhone());
wxOrder.setRecipientName(orderVO.getRecipientName());
wxOrder.setReceivingAddress(orderVO.getReceivingAddress());
wxOrder.setHfSeqId(hf_seq_id);
mobileOrderMapper.insert(wxOrder);
Collection<MobileOrderItem> orderItems = BeanCopyUtils.copyArrays(orderItemVOS, MobileOrderItem::new);
orderItems.forEach(item -> item.setMobileOrderId(orderId));
mobileOrderItemMapper.insert(orderItems);
}else {
List<Integer> storeIds = orderItemVOS.stream().map(MobileOrderItemVO::getStoreId).distinct().collect(Collectors.toList());
Map<Integer,String> map = new HashMap<>();
for (Integer storeId : storeIds) {
String orderId = IdUtil.getSnowflakeNextIdStr();
map.put(storeId,orderId);
MobileOrder wxOrder = new MobileOrder();
wxOrder.setOrderId(orderId);
wxOrder.setOutTradeNo(outTradeNo);
BigDecimal total = orderItemVOS.stream()
.filter(vo -> storeId.equals(vo.getStoreId()))
.map(MobileOrderItemVO::getPrice)
.reduce(BigDecimal.ZERO, BigDecimal::add);
wxOrder.setTotalPrice(total);
wxOrder.setStatus(OrderStatus.unpaid.name());
wxOrder.setStoreId(storeId);
wxOrder.setCreateTime(LocalDateTime.now(Clock.systemDefaultZone()));
wxOrder.setRecipientName(orderVO.getRecipientName());
wxOrder.setRecipientPhone(orderVO.getRecipientPhone());
wxOrder.setReceivingAddress(orderVO.getReceivingAddress());
wxOrder.setHfSeqId(hf_seq_id);
mobileOrderMapper.insert(wxOrder);
}
for (MobileOrderItemVO itemVO : orderItemVOS) {
String orderId = map.get(itemVO.getStoreId());
MobileOrderItem orderItem = new MobileOrderItem();
BeanUtils.copyProperties(itemVO,orderItem);
orderItem.setMobileOrderId(orderId);
mobileOrderItemMapper.insert(orderItem);
}
}
/*//将订单添加到关闭订单延迟队列
wxOrderCloseScheduler.addCloseWxOrderTask(orderId, wxProperties.getPlaceOrder().getExpireTime() * 60 * 1000);
//开启轮询查询订单支付状态
try {
wxPayOrderService.startPayStatusQuery(orderId);
}catch (Exception e){
log.error("开启订单支付状态轮询查询异常",e);
}*/
WxPlaceOrderResVO placeOrderResVO = new WxPlaceOrderResVO();
String pay_info = responseData.getString("pay_info");
JSONObject payInfo = JSONObject.parseObject(pay_info);
placeOrderResVO.setTimestamp(payInfo.getString("timeStamp"));
placeOrderResVO.setNonceStr(payInfo.getString("nonceStr"));
placeOrderResVO.setSignType(payInfo.getString("signType"));
// placeOrderResVO.setOrderId(orderId);
placeOrderResVO.setPaySign(payInfo.getString("paySign"));
placeOrderResVO.setPrepayId(payInfo.getString("package").split("=")[1]);
return placeOrderResVO;
}
throw new ServiceException("下单失败");
}
@Override
public ResultData wxPaySuccessHandler(HttpServletRequest request, JSONObject body) {
return null;
}
@Override
public String configureApplet() {
JSONObject params = JSONObject.of();
params.put("sys_id",huiFuProperties.getHuifuId());
params.put("product_id",huiFuProperties.getProductId());
JSONObject data = JSONObject.of();
String reqSeqId = DateUtil.format(new Date(),"yyyyMMddHHmmss") + RandomStringUtils.randomNumeric(18);
data.put("req_seq_id",reqSeqId);
data.put("req_date",DateUtil.format(new Date(),"yyyyMMdd"));
data.put("huifu_id",huiFuProperties.getHuifuId());
data.put("fee_type","02");
data.put("wx_applet_app_id",wxProperties.getAppid());
data.put("wx_applet_secret",wxProperties.getSecret());
data.put("async_return_url",huiFuProperties.getConfigureAppletCallbackUrl());
params.put("data",data);
String sign = HuifuSignUtil.sign(data, huiFuProperties.getPrivateKey());
String url = "https://api.huifu.com/v2/merchant/busi/config";
params.put("sign",sign);
log.info("params={}",params);
String res = HttpUtil.doPostJson(url, null, params);
log.info("汇付微信商户配置小程序appid返回:{}",res);
return "success";
}
}
@@ -0,0 +1,439 @@
package cn.unizk.huifupay.service;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.unizk.config.WxProperties;
import cn.unizk.entity.dto.CashOutDTO;
import cn.unizk.entity.dto.CashOutResponse;
import cn.unizk.entity.po.*;
import cn.unizk.entity.vo.CourseVO;
import cn.unizk.entity.vo.PayCourseVO;
import cn.unizk.enums.OrderStatus;
import cn.unizk.exception.ServiceException;
import cn.unizk.huifupay.config.properties.HuiFuVendorProperties;
import cn.unizk.huifupay.dto.HuifuPlaceOrderParamBaseDTO;
import cn.unizk.huifupay.utils.HuifuSignUtil;
import cn.unizk.huifupay.vo.DeviceScanPayVO;
import cn.unizk.huifupay.vo.RechargeVO;
import cn.unizk.mapper.*;
import cn.unizk.utils.HttpUtil;
import cn.unizk.utils.HuifuUtil;
import cn.unizk.utils.JkSecurityUtil;
import cn.unizk.wxPay.vo.WxPlaceOrderResVO;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Objects;
/**
* @Author hao
* @Date 2026/3/11 10:53
* @Version 1.0
**/
@Service
@Slf4j
public class HuiFuVendorPayService {
@Resource
private HuiFuVendorProperties vendorProperties;
@Resource
private WxProperties wxProperties;
@Resource
private MobileOrderMapper mobileOrderMapper;
@Resource
private JkStoreMapper jkStoreMapper;
@Resource
private DeviceMapper deviceMapper;
@Resource
private CashOutRecordMapper cashOutRecordMapper;
@Resource
private CourseMapper courseMapper;
@Resource
private WxVipRechargeMapper vipRechargeMapper;
private static final String PAY_API = "https://api.huifu.com/v3/trade/payment/jspay";
private static final String YUE_PAY_API = "https://api.huifu.com/v2/trade/acctpayment/pay";
private static final String COMMON_PARAM = "commonParam";
private static final String AMOUNT = "amount";
private static final String REQ_SEQ_ID = "req_seq_id";
/**
* 聚合正扫,扫码付费
*
* @param vo
* @return
*/
@Transactional(rollbackFor = Exception.class)
public WxPlaceOrderResVO vendorPay(DeviceScanPayVO vo) {
if (StringUtils.isBlank(vo.getDeviceId())){
throw new ServiceException("deviceId cannot be empty");
}
Device device = deviceMapper.selectById(vo.getDeviceId());
if (Objects.isNull(device) || device.getStoreId() == null){
throw new ServiceException("设备不存在或设备未与门店绑定");
}
JSONObject data = JSONObject.of();
String now = DateUtil.format(new Date(), "yyyyMMdd");
data.put("req_date", now);
String snowId = IdUtil.getSnowflakeNextIdStr();
String reqSeqId = HuifuUtil.getReqSeqId();
data.put("req_seq_id", reqSeqId);
data.put("huifu_id", vendorProperties.getHuifuId());
data.put("goods_desc", vo.getGoodsDesc());
data.put("trade_type", "T_MINIAPP");
BigDecimal bd = new BigDecimal(vo.getAmount());
bd = bd.setScale(2, RoundingMode.DOWN);
data.put("trans_amt", bd.toString());
JSONObject wx_data = JSONObject.of();
wx_data.put("sub_appid", wxProperties.getAppid());
wx_data.put("sub_openid", vo.getOpenid());
data.put("wx_data", wx_data.toString());
data.put("fee_flag", 2);
data.put("notify_url", vendorProperties.getNotifyUrl());
String sign = HuifuSignUtil.sign(data, vendorProperties.getPrivateKey());
JSONObject commonParams = JSONObject.of();
commonParams.put("sys_id", vendorProperties.getSysId());
commonParams.put("product_id", vendorProperties.getProductId());
commonParams.put("sign", sign);
commonParams.put("data", data);
log.info("commonParams={}", commonParams);
String res = HttpUtil.doPostJson(PAY_API, null, commonParams);
log.info("汇付下单返回:{}", res);
JSONObject response = JSONObject.parseObject(res);
if (response == null) {
throw new ServiceException("下单失败");
}
JSONObject responseData = response.getJSONObject("data");
String resp_code = responseData.getString("resp_code");
String hf_seq_id = responseData.getString("hf_seq_id");
if ("00000100".equals(resp_code) || "00000000".equals(resp_code)) {
log.info("订单:{}下单成功", snowId);
MobileOrder wxOrder = new MobileOrder();
wxOrder.setOrderId(snowId);
wxOrder.setOutTradeNo(reqSeqId);
wxOrder.setTotalPrice(bd);
// wxOrder.setOpenid(vo.getOpenid());
wxOrder.setMobileUserId(vo.getUserId());
wxOrder.setStatus(OrderStatus.unpaid.name());
wxOrder.setDeviceId(vo.getDeviceId());
wxOrder.setDescription(vo.getGoodsDesc());
wxOrder.setDeliveryWay("自提");
wxOrder.setCreateTime(LocalDateTime.now());
wxOrder.setOrderType(1);
mobileOrderMapper.insert(wxOrder);
WxPlaceOrderResVO placeOrderResVO = new WxPlaceOrderResVO();
String pay_info = responseData.getString("pay_info");
JSONObject payInfo = JSONObject.parseObject(pay_info);
placeOrderResVO.setTimestamp(payInfo.getString("timeStamp"));
placeOrderResVO.setNonceStr(payInfo.getString("nonceStr"));
placeOrderResVO.setSignType(payInfo.getString("signType"));
// placeOrderResVO.setOrderId(orderId);
placeOrderResVO.setPaySign(payInfo.getString("paySign"));
placeOrderResVO.setPrepayId(payInfo.getString("package").split("=")[1]);
return placeOrderResVO;
}
throw new ServiceException("下单失败");
}
private JSONObject createPlaceOrderParams(HuifuPlaceOrderParamBaseDTO dto){
JSONObject data = JSONObject.of();
String now = DateUtil.format(new Date(), "yyyyMMdd");
data.put("req_date", now);
// String snowId = IdUtil.getSnowflakeNextIdStr();
String reqSeqId = HuifuUtil.getReqSeqId();
data.put("req_seq_id", reqSeqId);
data.put("huifu_id", vendorProperties.getHuifuId());
data.put("goods_desc", dto.getGoodsDesc());
data.put("trade_type", "T_MINIAPP");
BigDecimal bd = new BigDecimal(dto.getPlaceOrderAmount());
bd = bd.setScale(2, RoundingMode.DOWN);
data.put("trans_amt", bd.toString());
JSONObject wx_data = JSONObject.of();
wx_data.put("sub_appid", wxProperties.getAppid());
wx_data.put("sub_openid", dto.getOpenid());
data.put("wx_data", wx_data.toString());
data.put("fee_flag", 2);
data.put("notify_url", vendorProperties.getNotifyUrl());
String sign = HuifuSignUtil.sign(data, vendorProperties.getPrivateKey());
JSONObject commonParams = JSONObject.of();
commonParams.put("sys_id", vendorProperties.getSysId());
commonParams.put("product_id", vendorProperties.getProductId());
commonParams.put("sign", sign);
commonParams.put("data", data);
JSONObject of = JSONObject.of();
of.put(COMMON_PARAM,commonParams);
of.put(AMOUNT,bd);
of.put(REQ_SEQ_ID,reqSeqId);
return of;
}
public WxPlaceOrderResVO recharge(RechargeVO vo){
JSONObject data = JSONObject.of();
String now = DateUtil.format(new Date(), "yyyyMMdd");
data.put("req_date", now);
String snowId = IdUtil.getSnowflakeNextIdStr();
String reqSeqId = HuifuUtil.getReqSeqId();
data.put("req_seq_id", reqSeqId);
data.put("huifu_id", vendorProperties.getHuifuId());
data.put("goods_desc", "会员充值");
data.put("trade_type", "T_MINIAPP");
BigDecimal bd = new BigDecimal(vo.getRechargePrice());
bd = bd.setScale(2, RoundingMode.DOWN);
data.put("trans_amt", bd.toString());
JSONObject wx_data = JSONObject.of();
wx_data.put("sub_appid", wxProperties.getAppid());
wx_data.put("sub_openid", vo.getOpenid());
data.put("wx_data", wx_data.toString());
data.put("fee_flag", 2);
data.put("notify_url", vendorProperties.getNotifyUrl());
String sign = HuifuSignUtil.sign(data, vendorProperties.getPrivateKey());
JSONObject commonParams = JSONObject.of();
commonParams.put("sys_id", vendorProperties.getSysId());
commonParams.put("product_id", vendorProperties.getProductId());
commonParams.put("sign", sign);
commonParams.put("data", data);
log.info("commonParams={}", commonParams);
String res = HttpUtil.doPostJson(PAY_API, null, commonParams);
log.info("汇付下单返回:{}", res);
JSONObject response = JSONObject.parseObject(res);
if (response == null) {
throw new ServiceException("下单失败");
}
JSONObject responseData = response.getJSONObject("data");
String resp_code = responseData.getString("resp_code");
if ("00000100".equals(resp_code) || "00000000".equals(resp_code)) {
log.info("订单:{}下单成功", snowId);
MobileOrder wxOrder = new MobileOrder();
wxOrder.setOrderId(snowId);
wxOrder.setOutTradeNo(reqSeqId);
wxOrder.setTotalPrice(bd);
// wxOrder.setOpenid(vo.getOpenid());
wxOrder.setMobileUserId(vo.getUserId());
wxOrder.setStatus(OrderStatus.unpaid.name());
// wxOrder.setDeviceId(vo.getDeviceId());
wxOrder.setDescription("会员充值");
wxOrder.setDeliveryWay("自提");
wxOrder.setCreateTime(LocalDateTime.now());
wxOrder.setOrderType(4);
mobileOrderMapper.insert(wxOrder);
WxVipRecharge vipRecharge = new WxVipRecharge();
vipRecharge.setOpenid(vo.getOpenid());
vipRecharge.setOrderId(snowId);
vipRecharge.setRechargeAmount(bd);
BigDecimal giftAmount = new BigDecimal(vo.getGiftAmount());
BigDecimal decimalGift = giftAmount.setScale(2, RoundingMode.DOWN);
vipRecharge.setPresentAmount(decimalGift);
vipRecharge.setPayStatus(0);
vipRecharge.setCreateTime(LocalDateTime.now());
vipRechargeMapper.insert(vipRecharge);
WxPlaceOrderResVO placeOrderResVO = new WxPlaceOrderResVO();
String pay_info = responseData.getString("pay_info");
JSONObject payInfo = JSONObject.parseObject(pay_info);
placeOrderResVO.setTimestamp(payInfo.getString("timeStamp"));
placeOrderResVO.setNonceStr(payInfo.getString("nonceStr"));
placeOrderResVO.setSignType(payInfo.getString("signType"));
// placeOrderResVO.setOrderId(orderId);
placeOrderResVO.setPaySign(payInfo.getString("paySign"));
placeOrderResVO.setPrepayId(payInfo.getString("package").split("=")[1]);
return placeOrderResVO;
}
throw new ServiceException("会员充值下单失败");
}
public WxPlaceOrderResVO activeMembership(HuifuPlaceOrderParamBaseDTO dto){
JSONObject jsonObject = createPlaceOrderParams(dto);
JSONObject placeOrderParams = (JSONObject) jsonObject.get(COMMON_PARAM);
String reqSeqId = jsonObject.getString(REQ_SEQ_ID);
BigDecimal bd = (BigDecimal) jsonObject.get(AMOUNT);
String res = HttpUtil.doPostJson(PAY_API, null, placeOrderParams);
log.info("汇付下单返回:{}", res);
JSONObject response = JSONObject.parseObject(res);
if (response == null) {
throw new ServiceException("下单失败");
}
JSONObject responseData = response.getJSONObject("data");
String resp_code = responseData.getString("resp_code");
if ("00000100".equals(resp_code) || "00000000".equals(resp_code)) {
String snowId = IdUtil.getSnowflakeNextIdStr();
log.info("订单:{}下单成功", snowId);
MobileOrder wxOrder = new MobileOrder();
wxOrder.setOrderId(snowId);
wxOrder.setOutTradeNo(reqSeqId);
wxOrder.setTotalPrice(bd);
// wxOrder.setOpenid(dto.getOpenid());
wxOrder.setMobileUserId(dto.getUserId());
wxOrder.setStatus(OrderStatus.unpaid.name());
// wxOrder.setDeviceId(vo.getDeviceId());
wxOrder.setDescription(dto.getGoodsDesc());
wxOrder.setDeliveryWay("自提");
wxOrder.setCreateTime(LocalDateTime.now());
wxOrder.setOrderType(6);
mobileOrderMapper.insert(wxOrder);
WxPlaceOrderResVO placeOrderResVO = new WxPlaceOrderResVO();
String pay_info = responseData.getString("pay_info");
JSONObject payInfo = JSONObject.parseObject(pay_info);
placeOrderResVO.setTimestamp(payInfo.getString("timeStamp"));
placeOrderResVO.setNonceStr(payInfo.getString("nonceStr"));
placeOrderResVO.setSignType(payInfo.getString("signType"));
// placeOrderResVO.setOrderId(orderId);
placeOrderResVO.setPaySign(payInfo.getString("paySign"));
placeOrderResVO.setPrepayId(payInfo.getString("package").split("=")[1]);
return placeOrderResVO;
}
throw new ServiceException("开通会员下单失败");
}
/**
* 门店提现接口
*
* @param dto
* @return
*/
public CashOutResponse profitSharingPay(CashOutDTO dto) {
JSONObject data = JSONObject.of();
String snowId = IdUtil.getSnowflakeNextIdStr();
data.put("req_seq_id", snowId);
String now = DateUtil.format(new Date(), "yyyyMMdd");
data.put("req_date", now);
data.put("out_huifu_id", vendorProperties.getHuifuId());
BigDecimal bd = new BigDecimal(dto.getAmount());
bd = bd.setScale(2, RoundingMode.DOWN);
data.put("ord_amt", bd.toString());
JkStore jkStore = jkStoreMapper.selectById(dto.getStoreId());
if (Objects.isNull(jkStore)) {
return new CashOutResponse(500,String.format("门店ID【%d】不存在",dto.getStoreId()));
}
JSONObject acct_split_bunch = JSONObject.of();
JSONArray acct_infos = new JSONArray();
JSONObject accInfo = new JSONObject();
accInfo.put("div_amt",bd.toString());
accInfo.put("huifu_id", jkStore.getHuifuId());
// accInfo.put("percentage_div", jkStore.getProfitSharingPercentage().toString());
acct_infos.add(accInfo);
/*JSONObject master_acct = new JSONObject();
BigDecimal b100 = new BigDecimal("100.00");
BigDecimal surplusPercent = b100.subtract(jkStore.getProfitSharingPercentage());
log.info("主商户分账比例为:{}", surplusPercent);
master_acct.put("huifu_id", vendorProperties.getHuifuId());
master_acct.put("percentage_div", surplusPercent.toString());
acct_infos.add(master_acct);*/
acct_split_bunch.put("acct_infos", acct_infos);
acct_split_bunch.put("percentage_flag", "Y");
data.put("acct_split_bunch", acct_split_bunch);
String sign = HuifuSignUtil.sign(data, vendorProperties.getPrivateKey());
JSONObject commonParams = new JSONObject();
commonParams.put("sys_id", vendorProperties.getSysId());
commonParams.put("product_id", vendorProperties.getProductId());
commonParams.put("sign", sign);
commonParams.put("data", data);
log.info("参数:{}", commonParams);
String res = HttpUtil.doPostJson(YUE_PAY_API, null, commonParams);
log.info("余额支付接口返回数据:{}", res);
JSONObject response = JSONObject.parseObject(res);
if (response == null) {
return new CashOutResponse(300,"提现失败!");
}
JSONObject responseData = response.getJSONObject("data");
String resp_code = responseData.getString("resp_code");
String req_seq_id = responseData.getString("req_seq_id");
String req_date = responseData.getString("req_date");
String out_huifu_id = responseData.getString("out_huifu_id");
String ord_amt = responseData.getString("ord_amt");
if ("00000000".equals(resp_code)) {
log.info("提现成功", snowId);
CashOutRecord cashOutRecord = new CashOutRecord();
cashOutRecord.setAmount(bd);
cashOutRecord.setOutHuifuId(vendorProperties.getHuifuId());
cashOutRecord.setStoreId(dto.getStoreId());
cashOutRecord.setCreateTime(LocalDateTime.now());
cashOutRecordMapper.insert(cashOutRecord);
}
throw new ServiceException(String.format("余额支付接口返回状态码:%s,提现失败!",resp_code));
}
public WxPlaceOrderResVO coursePay(PayCourseVO vo){
Course course = courseMapper.selectById(vo.getCourseId());
if (Objects.isNull(course)){
throw new ServiceException("course is not exist");
}
int compare = course.getPrice().compareTo(new BigDecimal(vo.getPrice()));
if (compare != 0){
throw new ServiceException("课程价格错误!");
}
JSONObject data = JSONObject.of();
String now = DateUtil.format(new Date(), "yyyyMMdd");
data.put("req_date", now);
String snowId = IdUtil.getSnowflakeNextIdStr();
String reqSeqId = HuifuUtil.getReqSeqId();
data.put("req_seq_id", reqSeqId);
data.put("huifu_id", vendorProperties.getHuifuId());
data.put("goods_desc", "视频课程购买");
data.put("trade_type", "T_MINIAPP");
BigDecimal bd = new BigDecimal(vo.getPrice());
bd = bd.setScale(2, RoundingMode.DOWN);
data.put("trans_amt", bd.toString());
JSONObject wx_data = JSONObject.of();
wx_data.put("sub_appid", wxProperties.getAppid());
wx_data.put("sub_openid", vo.getOpenid());
data.put("wx_data", wx_data.toString());
data.put("fee_flag", 2);
data.put("notify_url", vendorProperties.getNotifyUrl());
String sign = HuifuSignUtil.sign(data, vendorProperties.getPrivateKey());
JSONObject commonParams = JSONObject.of();
commonParams.put("sys_id", vendorProperties.getSysId());
commonParams.put("product_id", vendorProperties.getProductId());
commonParams.put("sign", sign);
commonParams.put("data", data);
log.info("commonParams={}", commonParams);
String res = HttpUtil.doPostJson(PAY_API, null, commonParams);
log.info("课程购买下单返回:{}", res);
JSONObject response = JSONObject.parseObject(res);
if (response == null) {
throw new ServiceException("课程购买下单失败");
}
JSONObject responseData = response.getJSONObject("data");
String resp_code = responseData.getString("resp_code");
if ("00000100".equals(resp_code) || "00000000".equals(resp_code)){
log.info("购买课程下单成功,orderId:{}",snowId);
MobileOrder wxOrder = new MobileOrder();
wxOrder.setOrderId(snowId);
wxOrder.setOutTradeNo(reqSeqId);
wxOrder.setTotalPrice(bd);
wxOrder.setCourseId(vo.getCourseId());
// wxOrder.setOpenid(vo.getOpenid());
wxOrder.setMobileUserId(vo.getUserId());
wxOrder.setStatus(OrderStatus.unpaid.name());
wxOrder.setDescription("视频课程购买");
wxOrder.setDeliveryWay("自提");
wxOrder.setCreateTime(LocalDateTime.now());
wxOrder.setOrderType(3);
mobileOrderMapper.insert(wxOrder);
WxPlaceOrderResVO placeOrderResVO = new WxPlaceOrderResVO();
String pay_info = responseData.getString("pay_info");
JSONObject payInfo = JSONObject.parseObject(pay_info);
placeOrderResVO.setTimestamp(payInfo.getString("timeStamp"));
placeOrderResVO.setNonceStr(payInfo.getString("nonceStr"));
placeOrderResVO.setSignType(payInfo.getString("signType"));
// placeOrderResVO.setOrderId(orderId);
placeOrderResVO.setPaySign(payInfo.getString("paySign"));
placeOrderResVO.setPrepayId(payInfo.getString("package").split("=")[1]);
return placeOrderResVO;
}
throw new ServiceException("课程购买下单失败!");
}
}
@@ -0,0 +1,18 @@
package cn.unizk.huifupay.utils;
import cn.hutool.core.date.DateUtil;
import org.apache.commons.lang3.RandomStringUtils;
import java.util.Date;
/**
* @Author hao
* @Date 2025/9/29 15:02
* @Version 1.0
**/
public class HfUtil {
public static String getReqSeqId(){
return DateUtil.format(new Date(),"yyyyMMddHHmmss") + RandomStringUtils.randomNumeric(18);
}
}
@@ -0,0 +1,69 @@
package cn.unizk.huifupay.utils;
import cn.unizk.exception.ServiceException;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.TreeMap;
/**
* @Author hao
* @Date 2025/9/29 10:53
* @Version 1.0
**/
@Slf4j
public class HuifuSignUtil {
/**
* RSA私钥签名:签名方式SHA256WithRSA
* @param data 待签名字符串
* @param privateKeyBase64 私钥(Base64编码)
* @return 签名byte[]
* @throws ServiceException
*/
public static String sign(String data, String privateKeyBase64) {
// Base64 --> Key
try {
byte[] bytes = Base64.getDecoder().decode(privateKeyBase64);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
KeyFactory keyFactory;
keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
// Sign
Signature signature = Signature.getInstance("SHA256WithRSA");
signature.initSign(privateKey);
signature.update(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(signature.sign());
} catch (Exception e) {
log.error("签名异常",e);
throw new ServiceException("签名异常");
}
}
public static String sign(JSONObject object, String privateKeyStr) {
TreeMap treeMap = JSONObject.parseObject(object.toString(), TreeMap.class);
JSONObject signBody = new JSONObject(treeMap);
// Base64 --> Key
try {
byte[] bytes = Base64.getDecoder().decode(privateKeyStr);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
KeyFactory keyFactory;
keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
// Sign
Signature signature = Signature.getInstance("SHA256WithRSA");
signature.initSign(privateKey);
signature.update(signBody.toString().getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(signature.sign());
} catch (Exception e) {
log.error("签名异常",e);
throw new ServiceException("签名异常");
}
}
}
@@ -0,0 +1,22 @@
package cn.unizk.huifupay.vo;
import lombok.Data;
/**
* @Author hao
* @Date 2026/3/11 10:56
* @Version 1.0
**/
@Data
public class DeviceScanPayVO {
private String deviceId;
private String amount;
private String goodsDesc;
private Integer userId;
private String openid;
}
@@ -0,0 +1,17 @@
package cn.unizk.huifupay.vo;
import lombok.Data;
/**
* @Author hao
* @Date 2026/4/17 14:02
* @Version 1.0
**/
@Data
public class RechargeVO {
private String openid;
private Integer userId;
private String rechargePrice;
private String giftAmount;
}
@@ -0,0 +1,18 @@
package cn.unizk.huifupay.vo;
import lombok.Data;
/**
* @Author hao
* @Date 2026/6/17 15:49
* @Version 1.0
**/
@Data
public class ReportTypeVO {
private String code1;
private String code2;
private Integer result;
}
@@ -0,0 +1,56 @@
package cn.unizk.interceptor;
import cn.unizk.constant.WxConstant;
import cn.unizk.entity.po.MobileAppUser;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.utils.JwtUtil;
import cn.unizk.utils.ServletUtils;
import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Objects;
/**
* @Author hao
* @Date 2025/8/21 14:46
* @Version 1.0
**/
@Component
public class AppletLoginInterceptor implements HandlerInterceptor {
@Autowired
@Qualifier("kryoRedisTemplate")
private RedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader(WxConstant.APPLET_LOGIN_SIGN);
if (StringUtils.isBlank(token)){
toRes(response,400);
return false;
}
String phoneNumber = JwtUtil.parsePlaintext(token);
MobileAppUser wxUser = (MobileAppUser) redisTemplate.opsForValue().get(WxConstant.CACHE_TOKEN_KEY + phoneNumber);
if (Objects.isNull(wxUser)){
toRes(response,401);
return false;
}
return true;
}
private void toRes(HttpServletResponse response,Integer statusCode){
ResultData data = new ResultData();
data.setCode(statusCode);
data.setMessage("Authentication failed");
ServletUtils.renderString(response, JSONObject.toJSONString(data));
}
}
@@ -0,0 +1,6 @@
package cn.unizk.mapper;
import cn.unizk.entity.po.Combo;
public interface ComboMapper extends UniBaseMapper<Combo> {
}
@@ -0,0 +1,17 @@
package cn.unizk.mapper;
import cn.unizk.entity.po.Conversation;
import org.apache.ibatis.annotations.Mapper;
public interface ConversationMapper {
void insertConversation(Conversation conversation);
void updateConversation(Conversation conversation);
Conversation selectBySessionId(String sessionId);
}
@@ -0,0 +1,11 @@
package cn.unizk.mapper;
import cn.unizk.entity.po.Device;
/**
* @Author hao
* @Date 2025/8/30 17:06
* @Version 1.0
**/
public interface DeviceMapper extends UniBaseMapper<Device> {
}
@@ -0,0 +1,11 @@
package cn.unizk.mapper;
import cn.unizk.entity.po.SysFile;
/**
* @Author hao
* @Date 2025/10/9 17:21
* @Version 1.0
**/
public interface FileMapper extends UniBaseMapper<SysFile> {
}
@@ -0,0 +1,7 @@
package cn.unizk.mapper;
import cn.unizk.entity.po.JkPhysio;
public interface JkPhysioMapper extends UniBaseMapper<JkPhysio> {
}
@@ -0,0 +1,7 @@
package cn.unizk.mapper;
import cn.unizk.entity.po.JkProduct;
public interface JkProductMapper extends UniBaseMapper<JkProduct> {
}
@@ -0,0 +1,6 @@
package cn.unizk.mapper;
import cn.unizk.entity.po.JkStore;
public interface JkStoreMapper extends UniBaseMapper<JkStore> {
}
@@ -0,0 +1,17 @@
package cn.unizk.mapper;
import cn.unizk.entity.po.Message;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
public interface MessageMapper {
void insertMessage(Message message);
void updateMessage(Message message);
List<Message> selectBySessionIdList(String sessionId);
}
@@ -0,0 +1,6 @@
package cn.unizk.mapper;
import cn.unizk.entity.po.ProductCarousel;
public interface ProductCarouselMapper extends UniBaseMapper<ProductCarousel> {
}
@@ -0,0 +1,6 @@
package cn.unizk.mapper;
import cn.unizk.entity.po.Question;
public interface QuestionMapper extends UniBaseMapper<Question> {
}
@@ -0,0 +1,16 @@
package cn.unizk.mapper;
import cn.unizk.entity.po.SysDict;
public interface SysDictMapper extends UniBaseMapper<SysDict> {
int deleteByPrimaryKey(Integer dictCode);
int insertSelective(SysDict record);
SysDict selectByPrimaryKey(Integer dictCode);
int updateByPrimaryKeySelective(SysDict record);
int updateByPrimaryKey(SysDict record);
}
@@ -0,0 +1,6 @@
package cn.unizk.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface UniBaseMapper<T> extends BaseMapper<T> {
}
@@ -0,0 +1,21 @@
/*
package cn.unizk.mapper;
import cn.unizk.entity.po.WxOrder;
import cn.unizk.entity.vo.OrderReqVO;
import cn.unizk.entity.vo.WxOrderVO;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
public interface WxOrderMapper extends UniBaseMapper<WxOrder> {
Integer updateStatusToPaid(@Param("orderId") String orderId);
Integer updateStatusToCanceled(@Param("orderId") String orderId);
Page<WxOrderVO> selectOrders(Page<WxOrderVO> page, @Param("vo") OrderReqVO vo);
WxOrder selectOrderByIdAndPhone(@Param("orderId") String orderId,@Param("phone") String phone);
}
*/
@@ -0,0 +1,11 @@
/*
package cn.unizk.mapper;
import cn.unizk.entity.po.WxUser;
import org.apache.ibatis.annotations.Param;
public interface WxUserMapper extends UniBaseMapper<WxUser> {
Integer bindPhone(@Param("phoneNumber") String phoneNumber,@Param("openid") String openid);
}
*/
@@ -0,0 +1,9 @@
package cn.unizk.mapper;
import cn.unizk.entity.po.WxUserSession;
import org.apache.ibatis.annotations.Param;
public interface WxUserSessionMapper extends UniBaseMapper<WxUserSession> {
Long selectMaxSessionId(@Param("openid") String openid);
}
@@ -0,0 +1,105 @@
package cn.unizk.scheduler;
import cn.unizk.entity.po.Chapter;
import cn.unizk.entity.po.MobileUserVideoProgress;
import cn.unizk.mapper.ChapterMapper;
import cn.unizk.mapper.MobileUserVideoProgressMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.Set;
/**
* @Author hao
* @Date 2026/3/27 15:44
* @Version 1.0
**/
@Component
@Slf4j
public class VideoProgressScheduler {
@Autowired
@Qualifier("kryoRedisTemplate")
private RedisTemplate redisTemplate;
@Resource
private MobileUserVideoProgressMapper mobileUserVideoProgressMapper;
@Resource
private ChapterMapper chapterMapper;
// @Scheduled(cron = "0 0/1 * * * ?") // 每10分钟执行一次
public void flushProgressToMySQL() {
String pattern = "video:progress:batch:*";
Set<String> keys = redisTemplate.keys(pattern);
if (keys == null || keys.isEmpty()) {
return;
}
for (String key : keys) {
try {
// 2. 获取数据
HashOperations<String, String, Object> ops = redisTemplate.opsForHash();
Object currentTimeObj = ops.get(key, "lastPosition");
Object durationObj = ops.get(key, "spentTime");
if (currentTimeObj == null || durationObj == null) continue;
int currentTime = Integer.parseInt(currentTimeObj.toString());
int spentTime = Integer.parseInt(durationObj.toString());
// 3. 解析 Key 获取 UserID 和 VideoID
// key format: video:progress:batch:{userId}:{videoId}
String[] parts = key.split(":");
if (parts.length < 5) continue;
Integer userId = Integer.valueOf(parts[3]);
Integer chapterId = Integer.valueOf(parts[4]);
MobileUserVideoProgress videoProgress = mobileUserVideoProgressMapper.selectOne(new LambdaQueryWrapper<MobileUserVideoProgress>()
.eq(MobileUserVideoProgress::getChapterId, chapterId)
.eq(MobileUserVideoProgress::getMobileUserId, userId));
if (Objects.isNull(videoProgress)){
MobileUserVideoProgress progress = new MobileUserVideoProgress();
progress.setChapterId(chapterId);
progress.setMobileUserId(userId);
progress.setSpentTime(spentTime);
progress.setLastPosition(currentTime);
progress.setUpdateTime(LocalDateTime.now());
Chapter chapter = chapterMapper.selectById(chapterId);
Integer p = (currentTime * 100) / chapter.getVideoDuration();
progress.setProgress(p);
mobileUserVideoProgressMapper.insert(progress);
}else {
videoProgress.setSpentTime(spentTime);
if (currentTime > videoProgress.getLastPosition()){
videoProgress.setLastPosition(currentTime);
Chapter chapter = chapterMapper.selectById(chapterId);
Integer p = (currentTime * 100) / chapter.getVideoDuration();
videoProgress.setProgress(p);
}
videoProgress.setUpdateTime(LocalDateTime.now());
mobileUserVideoProgressMapper.updateById(videoProgress);
}
// 5. 写入成功后,删除 Redis Key (防止重复计算)
redisTemplate.delete(key);
log.info("Flushed progress: userId={}, videoId={}, duration={}", userId, chapterId, spentTime);
} catch (Exception e) {
log.error("Flush progress failed for key: {}", key, e);
// 注意:这里不要删除 Key,让它留在 Redis 里,下次定时任务再试,或者依靠过期时间自动清理
}
}
}
}
@@ -0,0 +1,123 @@
/*
package cn.unizk.scheduler;
import cn.unizk.config.WxProperties;
import cn.unizk.constant.WxConstant;
import cn.unizk.entity.po.WxOrder;
import cn.unizk.exception.ServiceException;
import cn.unizk.mapper.WxOrderMapper;
import cn.unizk.service.PayService;
import cn.unizk.utils.HttpUtil;
import cn.unizk.wxPay.utils.WxSignatureUtil;
import com.alibaba.fastjson2.JSONObject;
import com.wechat.pay.java.core.http.HttpMethod;
import com.wechat.pay.java.core.util.NonceUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
*/
/**
* @Author hao
* @Date 2025/9/1 11:46
* @Version 1.0
**//*
@Component
@SuppressWarnings("unchecked")
@Slf4j
public class WxOrderCloseScheduler {
@Autowired
@Qualifier("kryoRedisTemplate")
private RedisTemplate redisTemplate;
@Resource
private WxOrderMapper wxOrderMapper;
@Resource
private WxProperties wxProperties;
@Value("${spring.profiles.active}")
private String active;
private static final String DELAY_QUEUE_KEY = "wxorder:close:delay";
public void addCloseWxOrderTask(String orderId,long delayMillis){
redisTemplate.opsForZSet().add(DELAY_QUEUE_KEY,orderId,System.currentTimeMillis() + delayMillis);
}
*/
/**
* 每10s执行一次
*//*
// @Scheduled(fixedRate = 10000)
public void processCloseWxOrderTasks(){
long now = System.currentTimeMillis();
Set<String> expireOrderIds = redisTemplate.opsForZSet().rangeByScore(DELAY_QUEUE_KEY,0,now,0,50);
if (CollectionUtils.isEmpty(expireOrderIds)){
return;
}
for (String expireOrderId : expireOrderIds) {
try {
closeOrder(expireOrderId);
redisTemplate.opsForZSet().remove(DELAY_QUEUE_KEY,expireOrderId);
}catch (Exception e){
log.error("关闭订单失败,orderId: {}", expireOrderId, e);
//将失败任务记录到死信队列中,再添加一个定时任务去消费死信队列中的记录
}
}
}
public void closeOrder(String orderId) {
WxOrder order = wxOrderMapper.selectById(orderId);
if (Objects.isNull(order) || !order.getStatus().equals("unpaid")){
log.info("wxorderclosescheduler--->订单已被关闭或已支付,{}",orderId);
return;
}
String authorization;
String nonce = NonceUtil.createNonce(32);
String closeOrderUrl = wxProperties.getPay().getCloseOrderApi().replace("{out_trade_no}", orderId);
String finalCloseOrderUrl = wxProperties.getPay().getDomain() + closeOrderUrl;
try {
WxSignatureUtil.setEnv(active);
authorization = WxSignatureUtil.getAuthorization(wxProperties.getPay().getMchid(),wxProperties.getPay().getSerialNo(),
nonce, HttpMethod.POST.name(),finalCloseOrderUrl,
JSONObject.toJSONString(JSONObject.of("mchid",wxProperties.getPay().getMchid())));
} catch (Exception e) {
e.printStackTrace();
throw new ServiceException("微信关闭订单生成签名失败");
}
Map<String,String> headers = new HashMap<>();
headers.put("Authorization", WxConstant.authorization_type + authorization);
headers.put("Accept","application/json");
headers.put("Content-Type","application/json");
JSONObject resp = HttpUtil.postJsonCarryStatusCode(finalCloseOrderUrl, headers, JSONObject.of("mchid", wxProperties.getPay().getMchid()));
int code = (int) resp.get(HttpUtil.STATUS_CODE);
if (code >= 200 && code < 300){
//对响应进行签名验证,参考WXPayUtility工具类。https://pay.weixin.qq.com/doc/v3/merchant/4014931831
//修改数据库订单状态为已关闭
WxOrder wxOrder = new WxOrder();
wxOrder.setOrderId(orderId);
wxOrder.setStatus("canceled");
wxOrderMapper.updateById(wxOrder);
log.info("关闭订单{}成功",orderId);
}else {
log.info("关闭订单失败,orderId={},微信关闭订单API返回:{}",orderId,resp);
}
}
}
*/
@@ -0,0 +1,18 @@
package cn.unizk.service;
import cn.unizk.entity.vo.ReplyVo;
import com.alibaba.fastjson2.JSONObject;
import java.util.List;
import java.util.Map;
public interface AIAdviceService{
ReplyVo reply (ReplyVo vo);
ReplyVo saveConversation(List<Map<String, List<String>>> conversation,String openid);
JSONObject getTreatmentPlan(String sessionId);
}
@@ -0,0 +1,7 @@
package cn.unizk.service;
import cn.unizk.entity.po.Combo;
import com.baomidou.mybatisplus.extension.service.IService;
public interface ComboService extends IService<Combo> {
}
@@ -0,0 +1,7 @@
package cn.unizk.service;
import cn.unizk.entity.po.Device;
import com.baomidou.mybatisplus.extension.service.IService;
public interface DeviceService extends IService<Device> {
}
@@ -0,0 +1,7 @@
package cn.unizk.service;
import cn.unizk.entity.po.SysFile;
import com.baomidou.mybatisplus.extension.service.IService;
public interface FileService extends IService<SysFile> {
}
@@ -0,0 +1,6 @@
package cn.unizk.service;
public interface IAiService {
}
@@ -0,0 +1,7 @@
package cn.unizk.service;
import cn.unizk.entity.po.JkPhysio;
import com.baomidou.mybatisplus.extension.service.IService;
public interface JkPhysioService extends IService<JkPhysio> {
}
@@ -0,0 +1,7 @@
package cn.unizk.service;
import cn.unizk.entity.po.JkProduct;
import com.baomidou.mybatisplus.extension.service.IService;
public interface JkProductService extends IService<JkProduct> {
}
@@ -0,0 +1,7 @@
package cn.unizk.service;
import cn.unizk.entity.po.JkStore;
import com.baomidou.mybatisplus.extension.service.IService;
public interface JkStoreService extends IService<JkStore> {
}
@@ -0,0 +1,20 @@
package cn.unizk.service;
import cn.unizk.entity.vo.MobileOrderVO;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.wxPay.vo.WxPlaceOrderResVO;
import com.alibaba.fastjson2.JSONObject;
import javax.servlet.http.HttpServletRequest;
public interface PayService {
WxPlaceOrderResVO wxPlaceOrder(MobileOrderVO orderVO);
ResultData wxPaySuccessHandler(HttpServletRequest request, JSONObject body);
default String configureApplet(){
return "success";
}
}
@@ -0,0 +1,7 @@
package cn.unizk.service;
import cn.unizk.entity.po.ProductCarousel;
import com.baomidou.mybatisplus.extension.service.IService;
public interface ProductCarouselService extends IService<ProductCarousel> {
}
@@ -0,0 +1,7 @@
package cn.unizk.service;
import cn.unizk.entity.po.Question;
import com.baomidou.mybatisplus.extension.service.IService;
public interface QuestionService extends IService<Question> {
}
@@ -0,0 +1,12 @@
package cn.unizk.service;
import cn.unizk.entity.po.SysDict;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @Author hao
* @Date 2025/9/3 11:22
* @Version 1.0
**/
public interface SysDictService extends IService<SysDict> {
}
@@ -0,0 +1,7 @@
package cn.unizk.service;
import cn.unizk.entity.po.MobileUserAddress;
import com.baomidou.mybatisplus.extension.service.IService;
public interface UserAddressService extends IService<MobileUserAddress> {
}
@@ -0,0 +1,208 @@
package cn.unizk.service;
import com.google.gson.Gson;
import okhttp3.HttpUrl;
import org.java_websocket.WebSocket;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CountDownLatch;
/**
* @Author hao
* @Date 2025/11/21 14:29
* @Version 1.0
**/
public class WebTtsWsService {
// 地址与鉴权信息
public static final String hostUrl = "https://tts-api.xfyun.cn/v2/tts";
// 均到控制台-语音合成页面获取
public static final String APPID = "3fa7b7a0";
public static final String APISecret = "N2Q5Y2NiOTZmNThhODZjYTgxMjg1NjA4";
public static final String APIKey = "da390c58ef0f2ad8424c3dccc6d458c7";
// 合成文本
public static String text = "欢迎来到讯飞开放平台";
// 合成文本编码格式
public static final String TTE = "UTF8"; // 小语种必须使用UNICODE编码作为值
// 发音人参数。到控制台-我的应用-语音合成-添加试用或购买发音人,添加后即显示该发音人参数值,若试用未添加的发音人会报错11200
public static final String VCN = "x4_yezi";
// 合成文件存储地址以及名称
public static final String OUTPUT_FILE_PATH = "D:\\code\\temp\\" + System.currentTimeMillis() + ".mp3";
// json
public static final Gson gson = new Gson();
public static volatile boolean wsCloseFlag = false;
/*public static void main(String[] args) throws Exception {
String wsUrl = getAuthUrl(hostUrl, APIKey, APISecret).replace("https://", "wss://");
OutputStream outputStream = new FileOutputStream(OUTPUT_FILE_PATH);
websocketWork(wsUrl, outputStream);
}*/
public static void tts(String text,String filePath,CountDownLatch latch) throws Exception {
WebTtsWsService.text = text;
OutputStream outputStream = new FileOutputStream(filePath);
String wsUrl = getAuthUrl(hostUrl, APIKey, APISecret).replace("https://", "wss://");
websocketWork(wsUrl,outputStream,latch);
}
// Websocket方法
private static void websocketWork(String wsUrl, OutputStream outputStream,CountDownLatch latch) {
try {
URI uri = new URI(wsUrl);
WebSocketClient webSocketClient = new WebSocketClient(uri) {
@Override
public void onOpen(ServerHandshake serverHandshake) {
System.out.println("ws建立连接成功...");
}
@Override
public void onMessage(String text) {
// System.out.println("本次转换的内容是:"+ text);
JsonParse myJsonParse = gson.fromJson(text, JsonParse.class);
if (myJsonParse.code != 0) {
System.out.println("发生错误,错误码为:" + myJsonParse.code);
System.out.println("本次请求的sid为:" + myJsonParse.sid);
}
if (myJsonParse.data != null) {
try {
byte[] textBase64Decode = Base64.getDecoder().decode(myJsonParse.data.audio);
outputStream.write(textBase64Decode);
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}
if (myJsonParse.data.status == 2) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("本次请求的sid==>" + myJsonParse.sid);
System.out.println("合成成功,文件保存路径为==>" + OUTPUT_FILE_PATH);
// 可以关闭连接,释放资源
wsCloseFlag = true;
latch.countDown();
}
}
}
@Override
public void onClose(int i, String s, boolean b) {
System.out.println("ws链接已关闭,本次请求完成...");
}
@Override
public void onError(Exception e) {
System.out.println("发生错误 " + e.getMessage());
}
};
// 建立连接
webSocketClient.connect();
while (!webSocketClient.getReadyState().equals(WebSocket.READYSTATE.OPEN)) {
//System.out.println("正在连接...");
Thread.sleep(100);
}
MyThread webSocketThread = new MyThread(webSocketClient);
webSocketThread.start();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
// 线程来发送音频与参数
static class MyThread extends Thread {
WebSocketClient webSocketClient;
public MyThread(WebSocketClient webSocketClient) {
this.webSocketClient = webSocketClient;
}
public void run() {
System.out.println("多线程发送文字内容------");
wsCloseFlag = false;
String requestJson;//请求参数json串
try {
requestJson = "{\n" +
" \"common\": {\n" +
" \"app_id\": \"" + APPID + "\"\n" +
" },\n" +
" \"business\": {\n" +
" \"aue\": \"lame\",\n" +
" \"sfl\": 1,\n" +
" \"tte\": \"" + TTE + "\",\n" +
" \"ent\": \"intp65\",\n" +
" \"vcn\": \"" + VCN + "\",\n" +
" \"pitch\": 50,\n" +
" \"speed\": 39\n" +
" },\n" +
" \"data\": {\n" +
" \"status\": 2,\n" +
" \"text\": \"" + Base64.getEncoder().encodeToString(text.getBytes(StandardCharsets.UTF_8)) + "\"\n" +
//" \"text\": \"" + Base64.getEncoder().encodeToString(TEXT.getBytes("UTF-16LE")) + "\"\n" +
" }\n" +
"}";
webSocketClient.send(requestJson);
// 等待服务端返回完毕后关闭
while (!wsCloseFlag) {
Thread.sleep(200);
}
webSocketClient.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 鉴权方法
public static String getAuthUrl(String hostUrl, String apiKey, String apiSecret) throws Exception {
URL url = new URL(hostUrl);
// 时间
SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
String date = format.format(new Date());
// 拼接
String preStr = "host: " + url.getHost() + "\n" +
"date: " + date + "\n" +
"GET " + url.getPath() + " HTTP/1.1";
//System.out.println(preStr);
// SHA256加密
Mac mac = Mac.getInstance("hmacsha256");
SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "hmacsha256");
mac.init(spec);
byte[] hexDigits = mac.doFinal(preStr.getBytes(StandardCharsets.UTF_8));
// Base64加密
String sha = Base64.getEncoder().encodeToString(hexDigits);
// 拼接
String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha);
// 拼接地址
HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.parse("https://" + url.getHost() + url.getPath())).newBuilder().//
addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorization.getBytes(StandardCharsets.UTF_8))).//
addQueryParameter("date", date).//
addQueryParameter("host", url.getHost()).//
build();
return httpUrl.toString();
}
//返回的json结果拆解
class JsonParse {
int code;
String sid;
Data data;
}
class Data {
int status;
String audio;
}
}
@@ -0,0 +1,7 @@
package cn.unizk.service;
import com.alibaba.fastjson2.JSONObject;
public interface WxAppletLoginService {
JSONObject appletLogin(String code);
}
@@ -0,0 +1,65 @@
package cn.unizk.service;
import cn.unizk.cache.LocalCache;
import cn.unizk.constant.WxConstant;
import cn.unizk.exception.ServiceException;
import cn.unizk.utils.HttpUtil;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @Author hao
* @Date 2025/7/9 13:41
* @Version 1.0
**/
@Service
@Slf4j
public class WxAuthService {
@Value("${wx.appid}")
private String appid;
@Value("${wx.secret}")
private String secret;
@Autowired
@Qualifier("kryoRedisTemplate")
private RedisTemplate redisTemplate;
private void refreshToken(){
Map<String,String> params = new HashMap<>();
params.put("grant_type","client_credential");
params.put("appid",appid);
params.put("secret",secret);
String s = HttpUtil.doPost(WxConstant.GET_ACCESS_TOKEN_URL, null, params);
log.info("wx获取access_token返回数据:{}",s);
try {
JSONObject getTokenRes = JSONObject.parseObject(s);
String access_token = (String) getTokenRes.get("access_token");
Integer expires_in = (Integer) getTokenRes.get("expires_in");
long exp = System.currentTimeMillis()/1000 + expires_in;
// LocalCache.setWxAccessTokenCache(access_token,exp);
redisTemplate.opsForValue().set(WxConstant.WX_ACCESS_TOKEN,access_token,5400, TimeUnit.SECONDS);
}catch (Exception e){
e.printStackTrace();
throw new ServiceException("获取微信token失败");
}
}
public String getAccessToken(){
String accessToken = (String) redisTemplate.opsForValue().get(WxConstant.WX_ACCESS_TOKEN);
if (StringUtils.isBlank(accessToken)){
refreshToken();
accessToken = (String) redisTemplate.opsForValue().get(WxConstant.WX_ACCESS_TOKEN);
}
return accessToken;
}
}
@@ -0,0 +1,14 @@
package cn.unizk.service;
/*
import cn.unizk.entity.po.WxOrder;
import cn.unizk.entity.vo.OrderReqVO;
import cn.unizk.entity.vo.UniPage;
import cn.unizk.entity.vo.WxOrderVO;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
public interface WxOrderService extends IService<WxOrder> {
Page<WxOrderVO> orderList(UniPage<OrderReqVO> uniPage);
WxOrder getOrderByIdAndPhone(String orderId,String phone);
}*/
@@ -0,0 +1,10 @@
package cn.unizk.service;
import cn.unizk.entity.po.MobileAppUser;
import com.baomidou.mybatisplus.extension.service.IService;
public interface WxUserService extends IService<MobileAppUser> {
void bindPhone(String phone,Integer openid);
}
@@ -0,0 +1,8 @@
package cn.unizk.service;
import cn.unizk.entity.po.WxUserSession;
import com.baomidou.mybatisplus.extension.service.IService;
public interface WxUserSessionService extends IService<WxUserSession> {
Long findMaxSessionId(String openid);
}
@@ -0,0 +1,494 @@
package cn.unizk.service.impl;
import cn.hutool.json.JSONArray;
import cn.unizk.entity.po.Conversation;
import cn.unizk.entity.po.Message;
import cn.unizk.entity.vo.ReplyVo;
import cn.unizk.mapper.ConversationMapper;
import cn.unizk.mapper.MessageMapper;
import cn.unizk.service.AIAdviceService;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.util.*;
/**
* @Author lj
* @Date 2025/7/149 16:30
* @Version 1.0
**/
@Service
@Slf4j
@RequiredArgsConstructor
@EnableTransactionManagement
public class AIAdviceServiceImpl implements AIAdviceService {
private static final String DP_API_KEY = "sk-1338e30687cd47d3adda5ae9d903d319"; //刘杰
// private static final String DP_API_URL = "https://api.deepseek.com";
private static final String DP_API_URL = "https://api.deepseek.com/chat/completions";
private final RestTemplate restTemplate = new RestTemplate();
private final ObjectMapper objectMapper = new ObjectMapper();
// 使用正确的模型名称
// private static final String MODEL_NAME = "deepseek-reasoner"; // r1
private static final String MODEL_NAME = "deepseek-chat"; // 多轮文档
private final ConversationMapper conversationMapper;
private final MessageMapper messageMapper;
// 问题到描述短语的映射
private static final Map<String, String> QUESTION_MAPPING = new HashMap<>();
// 疼痛位置到炎症名词的映射
private static final Map<String, String> PAIN_LOCATION_MAPPING = new HashMap<>();
static {
QUESTION_MAPPING.put("性别", "性别");
QUESTION_MAPPING.put("年龄", "年龄");
QUESTION_MAPPING.put("身高", "身高");
QUESTION_MAPPING.put("体重", "体重");
QUESTION_MAPPING.put("1.您膝关节疼痛的具体位置是哪里?", "膝关节疼痛的具体位置是");
QUESTION_MAPPING.put("2.您的膝关节疼痛持续了多长时间?", "膝关节疼痛持续");
QUESTION_MAPPING.put("3.您当前的膝关节疼痛程度如何?", "当前膝关节疼痛程度为");
QUESTION_MAPPING.put("4.您的膝关节主要是哪种类型的疼痛?", "膝关节疼痛类型为");
QUESTION_MAPPING.put("5.在哪些特定动作或姿势下您的膝关节疼痛会明显出现或加重?", "膝关节疼痛在以下情况加重:");
QUESTION_MAPPING.put("6.您的膝关节疼痛是否会向其他部位扩散或放射?", "膝关节疼痛扩散情况:");
QUESTION_MAPPING.put("7.您的膝关节还有哪些伴随症状?", "膝关节伴随症状:");
QUESTION_MAPPING.put("8.您的膝关节在哪些活动方向上感到受限?", "膝关节活动受限方向:");
QUESTION_MAPPING.put("9.您是否感觉膝关节不稳定?", "膝关节不稳定表现:");
QUESTION_MAPPING.put("10.您认为导致您膝关节疼痛的主要原因是什么?", "导致膝关节疼痛的主要原因:");
// 初始化疼痛位置映射
PAIN_LOCATION_MAPPING.put("位置1", "股四头肌肌腱炎");
PAIN_LOCATION_MAPPING.put("位置2", "股内侧肌拉伤");
PAIN_LOCATION_MAPPING.put("位置3", "股直肌肌腱炎髌上囊囊肿");
PAIN_LOCATION_MAPPING.put("位置4", "膝关节外侧疼痛");
PAIN_LOCATION_MAPPING.put("位置5", "髌骨外侧疼痛");
PAIN_LOCATION_MAPPING.put("位置6", "髌骨综合征");
PAIN_LOCATION_MAPPING.put("位置7", "髌骨内侧疼痛");
PAIN_LOCATION_MAPPING.put("位置8", "膝关节内侧疼痛");
PAIN_LOCATION_MAPPING.put("位置9", "半月板损伤");
PAIN_LOCATION_MAPPING.put("位置10", "脂肪垫炎");
PAIN_LOCATION_MAPPING.put("位置11", "髌腱炎");
PAIN_LOCATION_MAPPING.put("位置12", "胫骨骨骺炎");
PAIN_LOCATION_MAPPING.put("位置13", "鹅足滑囊炎");
}
private static final String SYSTEM_PROMPT = "你是一位专业的膝关节医疗顾问,我会提供收集的用户信息,请你根据信息给出符合用户病情的诊断参考报告。"
// + "报告内容包括:主诉、现病史、既往史、初步诊断、用药或治疗、饮食与运动、注意事项。要求详细、专业、只提供内容即可,不要多余的解释。";
+ "报告内容包括:一、初步诊断 二、病因及机制分析(1.诊断结论 2.形成原因) 三、康复建议。要求详细、专业、只提供内容即可,不要多余的解释。";
@Override
public ReplyVo reply(ReplyVo vo) {
//新
return vo;
}
@Override
@Transactional(rollbackFor = Exception.class)
public ReplyVo saveConversation(List<Map<String, List<String>>> conversation,String openid) {
ReplyVo vo = new ReplyVo();
if (validateParams(conversation, openid)) {
vo.setContent("参数错误");
return vo;
}
try {
// 1. 构建症状摘要
String summary = buildSymptomSummary(conversation);
// 2. 保存会话记录
Conversation conversationInfo = saveConversationToDB(conversation, openid, summary);
String sessionId = conversationInfo.getSessionId();
// 3. 构建AI请求消息
// List<Map<String, String>> messageList = buildMessageList(summary);
// 4. 获取AI诊断报告
// String response = getChatCompletion(messageList);
//25年 10-10日 第一版修改为不需要ai诊断,采用模板形式
String response = "模板";
// 5. 封装返回结果
vo.setContent(response);
vo.setSessionId(sessionId);
//将诊断回复存储到数据库
conversationInfo.setAiDiagnosis(response);
conversationMapper.updateConversation(conversationInfo);
} catch (Exception e) {
log.error("调用 DeepSeek API 失败", e);
throw new RuntimeException("出现错误,AI助手暂时无法响应您的请求,请稍后再试。", e);
}
return vo;
}
@Override
public JSONObject getTreatmentPlan(String sessionId){
//获取sessionId
// 1. 参数校验(使用具体异常而非空注释)
if (StringUtils.isBlank(sessionId)) {
throw new IllegalArgumentException("参数错误");
}
JSONObject plan = new JSONObject();
JSONObject errorResponse = new JSONObject();
try {
//根据参数去查询
Conversation conversation = conversationMapper.selectBySessionId(sessionId);
String qaJson = conversation.getQaJson();
//转为List<Map<String, String>> 来判断以及重组返回值
ObjectMapper objectMapper = new ObjectMapper();
List<Map<String, String>> qaList = objectMapper.readValue(
qaJson,
new TypeReference<List<Map<String, String>>>() {}
);
System.out.println(qaList);
System.out.println(qaList);
System.out.println(qaList);
plan.put("patientId", "P12345");
} catch (Exception e) {
errorResponse.put("content", "出错了" + e.getMessage());
// 9. 异常处理
return errorResponse;
}
return plan;
}
// --- 辅助方法 ---
private boolean validateParams(List<Map<String, List<String>>> conversation, String openid) {
return conversation == null || conversation.isEmpty() ||
openid == null || openid.isEmpty();
}
private String buildSymptomSummary(List<Map<String, List<String>>> conversation) {
StringBuilder summary = new StringBuilder("患者");
for (Map<String, List<String>> item : conversation) {
for (Map.Entry<String, List<String>> entry : item.entrySet()) {
String question = entry.getKey();
List<String> answers = entry.getValue();
// 获取预定义的描述短语
String description = QUESTION_MAPPING.getOrDefault(question, question);
// 特殊处理疼痛位置
if ("1.您膝关节疼痛的具体位置是哪里?".equals(question)) {
String processedAnswer = replacePainLocations(answers);
summary.append(description).append(processedAnswer).append(",");
continue;
}
// 处理多选答案,用"和"连接
String answerStr;
if (answers.size() == 1) {
answerStr = answers.get(0);
} else {
answerStr = String.join("和", answers);
}
summary.append(description).append(answerStr).append(",");
}
}
// 移除最后一个逗号
if (summary.length() > 0) {
summary.setLength(summary.length() - 1);
}
return summary.toString();
}
private Conversation saveConversationToDB(List<Map<String, List<String>>> conversation,
String openid, String summary) {
String sessionId = UUID.randomUUID().toString().replace("-", "");
Conversation conversationInfo = new Conversation();
conversationInfo.setSessionId(sessionId);
conversationInfo.setUserId(openid);
conversationInfo.setQaJson(conversation.toString());
conversationInfo.setSymptomSummary(summary);
conversationMapper.insertConversation(conversationInfo);
return conversationInfo;
}
private List<Map<String, String>> buildMessageList(String summary) {
List<Map<String, String>> messageList = new ArrayList<>();
// 系统消息
messageList.add(createMessage("system", SYSTEM_PROMPT));
// 用户消息
messageList.add(createMessage("user", "信息:" + summary));
return messageList;
}
private Map<String, String> createMessage(String role, String content) {
Map<String, String> message = new HashMap<>();
message.put("role", role);
message.put("content", content);
return message;
}
private String replacePainLocations(List<String> locations) {
List<String> replacedLocations = new ArrayList<>();
for (String loc : locations) {
String trimmedLoc = loc.trim();
// 检查是否是位置编码格式
if (trimmedLoc.startsWith("位置")) {
// 从映射表中获取医学名词,如果没有则返回原值
String medicalTerm = PAIN_LOCATION_MAPPING.get(trimmedLoc);
if (medicalTerm != null) {
replacedLocations.add(medicalTerm);
} else {
replacedLocations.add(trimmedLoc);
}
} else {
// 如果已经是医学名词,直接使用
replacedLocations.add(trimmedLoc);
}
}
// 用顿号连接处理后的位置
return String.join("、", replacedLocations);
}
// @Override
@Transactional(rollbackFor = Exception.class)
public ReplyVo reply000(ReplyVo vo) {
try {
// 使用实际的用户输入
String userMessage = vo.getContent(); // 实际应该使用 vo 中的内容
Integer sort = vo.getSort();
// 构造消息列表
List<Map<String, String>> userMessageList = new ArrayList<>();
// 系统提示词
Map<String, String> systemMessage = new HashMap<>();
String content ="你是一位专业的膝关节医疗顾问,我想打造一个智能化理疗问诊平台,通过动态流程引擎实现全自动问诊路径控制,确保在10分钟内高效完成从症状收集到初步诊断的全流程," +
"同时支持基于患者实时回答的智能分支跳转,保障问诊过程的专业性与时效性。一期建设主要实现膝盖疼痛、损伤等顽疾的诊断治疗。我会提供给你用户的回复,你根据最新的回复判断下一次提问的问题," +
"问题围绕这些维度展开:'主要症状','症状时长','伴随症状','损伤机制'" +
"只提供给我问题本身即可,不要多余的回复,例如:用户:我头疼。AI:头疼多久了?额外要求:若该回复和诊断膝盖无关,请输出'无关'二字,不需要多余的解释。";
// "2.若生成的问题为“膝盖疼痛位置”相关,则不输出问题,只回复'图片'二字,不需要多余的解释。" ;
//在下列7个维度中,
systemMessage.put("role", "system");
systemMessage.put("content", content);
userMessageList.add(systemMessage);
Message message = new Message();
String sessionId = "";
if(vo.getSort() == 1){
//第一次会话 生成会话id
sessionId = UUID.randomUUID().toString().replace("-", "");
Conversation conversation = new Conversation();
conversation.setSessionId(sessionId);
conversation.setUserId(vo.getOpenid());
//存储到会话表数据 后面ai判断无关时 更新状态
conversationMapper.insertConversation(conversation);
//存储到对话信息表
message.setSessionId(sessionId);
message.setSort(sort);
message.setAiMessage("请问有什么可以帮助你?");
message.setUserMessage(vo.getContent());
messageMapper.insertMessage(message);
}else {
sessionId = vo.getSessionId();
//查询最新的那条
//查询数据库是否有数据 状态为1 的有效数据 2无效数据
List<Message> messageList= messageMapper.selectBySessionIdList(sessionId);
//将messageList 所有数据组合到userMessageList 中
for (Message messageInfo : messageList) {
// 处理AI消息(assistant角色)
Map<String, String> assistantMsg = new HashMap<>();
assistantMsg.put("role", "assistant");
assistantMsg.put("content", messageInfo.getAiMessage()); // 使用Message对象的aiMessage字段
userMessageList.add(assistantMsg);
// 处理用户消息(user角色)
Map<String, String> userMsg = new HashMap<>();
userMsg.put("role", "user");
userMsg.put("content", messageInfo.getUserMessage()); // 使用Message对象的userMessage字段
userMessageList.add(userMsg);
}
}
//组装多轮对话
// 用户消息
Map<String, String> userMessageMap = new HashMap<>();
userMessageMap.put("role", "user");
userMessageMap.put("content", "用户回复:"+userMessage);
userMessageList.add(userMessageMap);
// Map<String, String> userMessageMap1 = new HashMap<>();
// userMessageMap1.put("role", "assistant");
// userMessageMap1.put("content", "请问您的膝盖是否有疼痛或不适感");
// userMessageList.add(userMessageMap1);
//
// Map<String, String> userMessageMap2 = new HashMap<>();
// userMessageMap2.put("role", "user");
// userMessageMap2.put("content", "涨得慌");
// userMessageList.add(userMessageMap2);
//
// Map<String, String> userMessageMap3 = new HashMap<>();
// userMessageMap3.put("role", "assistant");
// userMessageMap3.put("content", "您的膝盖是否有肿胀或发红的现象?");
// userMessageList.add(userMessageMap3);
//
// Map<String, String> userMessageMap4 = new HashMap<>();
// userMessageMap4.put("role", "user");
// userMessageMap4.put("content", "有");
// userMessageList.add(userMessageMap4);
//
//
// Map<String, String> userMessageMap5 = new HashMap<>();
// userMessageMap5.put("role", "assistant");
// userMessageMap5.put("content", "肿胀和发红的症状持续了多长时间?");
// userMessageList.add(userMessageMap5);
//
// Map<String, String> userMessageMap6 = new HashMap<>();
// userMessageMap6.put("role", "user");
// userMessageMap6.put("content", "好几天咧");
// userMessageList.add(userMessageMap6);
System.out.println(userMessageList);
String response = getChatCompletion(userMessageList);
// //得到答案后存储 数据库
Message nextMessage = new Message();
Integer nextSort = sort+1; //sort +1
if(("无关").equals(response)){
// 状态为2 更新status
// response =
}else {
nextMessage.setSessionId(sessionId);
nextMessage.setSort(nextSort);
}
System.out.println("AI 回复: " + response);
// 将 AI 回复设置到返回对象
vo.setContent(response);
vo.setSort(nextSort);
vo.setSessionId(sessionId);
} catch (Exception e) {
log.error("调用 DeepSeek API 失败", e);
vo.setContent("很抱歉,AI助手暂时无法响应您的请求,请稍后再试。");
throw new RuntimeException("出现错误,AI助手暂时无法响应您的请求,请稍后再试。", e);
}
return vo;
}
public String getChatCompletion( List<Map<String, String>> userMessageList) throws Exception {
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setBearerAuth(DP_API_KEY);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
// 构造请求体
Map<String, Object> body = new HashMap<>();
body.put("model", MODEL_NAME);
body.put("messages", userMessageList);
body.put("stream", false);
body.put("max_tokens", 1024);
// 构造请求实体
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(body, headers);
try {
// 发送请求
ResponseEntity<String> response = restTemplate.exchange(
DP_API_URL,
HttpMethod.POST,
requestEntity,
String.class
);
// 检查响应状态(处理非200状态码)
if (response.getStatusCode() != HttpStatus.OK) {
String errorMsg = "DeepSeek API 返回错误状态: " + response.getStatusCode();
log.error(errorMsg);
throw new Exception(errorMsg);
}
// 解析响应
Map<String, Object> result = objectMapper.readValue(response.getBody(), Map.class);
List<Map<String, Object>> choices = (List<Map<String, Object>>) result.get("choices");
Map<String, Object> message = (Map<String, Object>) choices.get(0).get("message");
return (String) message.get("content");
} catch (HttpStatusCodeException e) {
// 捕获所有HTTP状态异常 (4xx/5xx)
String errorMsg = String.format("DeepSeek API 错误: %d - %s",
e.getStatusCode().value(), e.getResponseBodyAsString());
log.error(errorMsg);
throw new Exception(errorMsg, e);
} catch (JsonProcessingException e) {
// 捕获JSON解析异常
String errorMsg = "响应JSON解析失败: " + e.getMessage();
log.error(errorMsg);
throw new Exception(errorMsg, e);
} catch (ClassCastException | NullPointerException e) {
// 捕获类型转换和空指针异常
String errorMsg = "API响应格式异常: " + e.getMessage();
log.error(errorMsg);
throw new Exception(errorMsg, e);
} catch (RestClientException e) {
// 捕获其他RestTemplate异常(网络问题等)
String errorMsg = "API请求失败: " + e.getMessage();
log.error(errorMsg);
throw new Exception(errorMsg, e);
} catch (Exception e) {
// 捕获所有其他未处理异常
String errorMsg = "未知API错误: " + e.getMessage();
log.error(errorMsg);
throw new Exception(errorMsg, e);
}
}
}
@@ -0,0 +1,16 @@
package cn.unizk.service.impl;
import cn.unizk.entity.po.Combo;
import cn.unizk.mapper.ComboMapper;
import cn.unizk.service.ComboService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* @Author hao
* @Date 2025/8/30 17:23
* @Version 1.0
**/
@Service
public class ComboServiceImpl extends ServiceImpl<ComboMapper, Combo> implements ComboService {
}
@@ -0,0 +1,16 @@
package cn.unizk.service.impl;
import cn.unizk.entity.po.Device;
import cn.unizk.mapper.DeviceMapper;
import cn.unizk.service.DeviceService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* @Author hao
* @Date 2025/8/30 17:07
* @Version 1.0
**/
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements DeviceService {
}
@@ -0,0 +1,54 @@
package cn.unizk.service.impl;
import cn.unizk.entity.vo.ResultData;
import cn.unizk.service.IAiService;
import com.volcengine.ark.runtime.model.responses.constant.ResponsesConstants;
import com.volcengine.ark.runtime.model.responses.content.InputContentItemText;
import com.volcengine.ark.runtime.model.responses.content.OutputContentItemText;
import com.volcengine.ark.runtime.model.responses.item.ItemEasyMessage;
import com.volcengine.ark.runtime.model.responses.item.ItemOutputMessage;
import com.volcengine.ark.runtime.model.responses.item.MessageContent;
import com.volcengine.ark.runtime.model.responses.request.CreateResponsesRequest;
import com.volcengine.ark.runtime.model.responses.request.ResponsesInput;
import com.volcengine.ark.runtime.model.responses.response.ResponseObject;
import com.volcengine.ark.runtime.service.ArkService;
import org.springframework.stereotype.Service;
/**
* @Author hao
* @Date 2026/5/12 16:36
* @Version 1.0
**/
@Service
public class DoubaoServiceImpl implements IAiService {
public String drawConclusion(String input1,String input2){
String apiKey = "ark-9da95e96-cffe-4e7d-afc5-771990873320-24c67";
// 创建ArkService实例
ArkService arkService = ArkService.builder().apiKey(apiKey).baseUrl("https://ark.cn-beijing.volces.com/api/v3").build();
CreateResponsesRequest request = CreateResponsesRequest.builder()
.model("doubao-seed-1-8-251228")
.input(ResponsesInput.builder().addListItem(
ItemEasyMessage.builder().role(ResponsesConstants.MESSAGE_ROLE_USER).content(
MessageContent.builder()
.addListItem(InputContentItemText.builder().text(input1).build())
.addListItem(InputContentItemText.builder().text(input2).build())
.build()
).build()
).build())
.build();
ResponseObject resp = arkService.createResponse(request);
System.out.println(resp);
String result = resp.getOutput().stream()
.filter(item -> item instanceof ItemOutputMessage)
.map(item -> (ItemOutputMessage) item)
.flatMap(msg -> msg.getContent().stream())
.filter(c -> c instanceof OutputContentItemText)
.map(c -> ((OutputContentItemText) c).getText())
.findFirst()
.orElse("获取答案失败");
System.out.println("result="+result);
arkService.shutdownExecutor();
return result;
}
}
@@ -0,0 +1,16 @@
package cn.unizk.service.impl;
import cn.unizk.entity.po.SysFile;
import cn.unizk.mapper.FileMapper;
import cn.unizk.service.FileService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* @Author hao
* @Date 2025/10/9 17:20
* @Version 1.0
**/
@Service
public class FileServiceImpl extends ServiceImpl<FileMapper, SysFile> implements FileService {
}
@@ -0,0 +1,18 @@
package cn.unizk.service.impl;
import cn.unizk.entity.po.JkPhysio;
import cn.unizk.mapper.JkPhysioMapper;
import cn.unizk.service.JkPhysioService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @Author hao
* @Date 2025/8/25 10:57
* @Version 1.0
**/
@Service
@Slf4j
public class JkPhysioServiceImpl extends ServiceImpl<JkPhysioMapper, JkPhysio> implements JkPhysioService {
}
@@ -0,0 +1,18 @@
package cn.unizk.service.impl;
import cn.unizk.entity.po.JkProduct;
import cn.unizk.mapper.JkProductMapper;
import cn.unizk.service.JkProductService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @Author hao
* @Date 2025/8/25 11:00
* @Version 1.0
**/
@Service
@Slf4j
public class JkProductServiceImpl extends ServiceImpl<JkProductMapper,JkProduct> implements JkProductService {
}
@@ -0,0 +1,18 @@
package cn.unizk.service.impl;
import cn.unizk.entity.po.JkStore;
import cn.unizk.mapper.JkStoreMapper;
import cn.unizk.service.JkStoreService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @Author hao
* @Date 2025/8/19 11:26
* @Version 1.0
**/
@Service
@Slf4j
public class JkStoreServiceImpl extends ServiceImpl<JkStoreMapper, JkStore> implements JkStoreService {
}
@@ -0,0 +1,16 @@
package cn.unizk.service.impl;
import cn.unizk.entity.po.ProductCarousel;
import cn.unizk.mapper.ProductCarouselMapper;
import cn.unizk.service.ProductCarouselService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* @Author hao
* @Date 2025/9/16 15:40
* @Version 1.0
**/
@Service
public class ProductCarouselServiceImpl extends ServiceImpl<ProductCarouselMapper, ProductCarousel> implements ProductCarouselService {
}
Loaded 100 of 1456 files, more files were not shown because too many files have changed in this diff. Show more