xuhy
2025-12-30 fe493ce3353faaa42708c1b85f61f398e3c6fa2f
AI对接
3个文件已添加
7个文件已修改
338 ■■■■■ 已修改文件
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/H5AICallbackController.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TSysAppUserController.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TSysAiConfig.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TSysAppUser.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/utils/util/HttpClientUtils.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/utils/xiMaUtil/AesUtil.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/utils/xiMaUtil/Hex.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/utils/xiMaUtil/XimaAIUtil.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TSysAiConfigMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/TSysAppUserMapper.xml 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/H5AICallbackController.java
@@ -15,6 +15,7 @@
import com.ruoyi.system.utils.util.RsaSimpleUtil;
import com.ruoyi.system.utils.wx.model.WeixinProperties;
import com.ruoyi.system.utils.wx.tools.WxAppletTools;
import com.ruoyi.system.utils.xiMaUtil.AesUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
@@ -127,4 +128,31 @@
        return "success";
    }
    @PostMapping("/xiMaReportPush")
    public String xiMaReportPush(JSONObject jsonObject) throws Exception {
        TSysAiConfig sysAiConfig = sysAiConfigService.getById(1);
        // 应用AES秘钥
        String clientKey = sysAiConfig.getClientKey();
        // 平台RSA公钥
        String clientIV = sysAiConfig.getClientIV();
        String data = jsonObject.getString("data");
        // 解密
        String raw = AesUtil.decrypt(clientKey, clientIV , data);
        log.info("H5舌面报告推送,解密后:{}",raw);
        return "success";
    }
    @PostMapping("/xiMaDoubleScreenReportPush")
    public JSONObject xiMaDoubleScreenReportPush(JSONObject jsonObject) throws Exception {
        log.info("双屏仪报告推送:{}",jsonObject);
        JSONObject response = new JSONObject();
        response.put("error_code",0);
        return response;
    }
}
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/TSysAppUserController.java
@@ -1,12 +1,11 @@
package com.ruoyi.web.controller.api;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.model.TSysAppUser;
@@ -17,6 +16,7 @@
import com.ruoyi.system.service.TSysChronicDiseaseService;
import com.ruoyi.system.service.TSysInspectionService;
import com.ruoyi.system.service.TSysOrderService;
import com.ruoyi.system.utils.xiMaUtil.XimaAIUtil;
import com.ruoyi.system.vo.TSysAppUserVO;
import com.ruoyi.system.vo.TSysOrderVO;
import io.swagger.annotations.Api;
@@ -28,6 +28,8 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
/**
 * <p>
@@ -48,14 +50,16 @@
    private final TSysChronicDiseaseService sysChronicDiseaseService;
    private final TokenService tokenService;
    private final RedisCache redisCache;
    private final XimaAIUtil ximaAIUtil;
    @Autowired
    public TSysAppUserController(TSysAppUserService sysAppUserService, TSysInspectionService sysInspectionService, TSysOrderService sysOrderService, TSysChronicDiseaseService sysChronicDiseaseService, TokenService tokenService, RedisCache redisCache) {
    public TSysAppUserController(TSysAppUserService sysAppUserService, TSysInspectionService sysInspectionService, TSysOrderService sysOrderService, TSysChronicDiseaseService sysChronicDiseaseService, TokenService tokenService, RedisCache redisCache, XimaAIUtil ximaAIUtil) {
        this.sysAppUserService = sysAppUserService;
        this.sysInspectionService = sysInspectionService;
        this.sysOrderService = sysOrderService;
        this.sysChronicDiseaseService = sysChronicDiseaseService;
        this.tokenService = tokenService;
        this.redisCache = redisCache;
        this.ximaAIUtil = ximaAIUtil;
    }
    /**
@@ -133,5 +137,26 @@
        return R.ok();
    }
    /**
     * 获取AI检测页面
     */
    @ApiOperation(value = "获取AI检测页面",notes = "设备类型 deviceType: android ios")
    @GetMapping(value = "/getXiMaAiDetection")
    public R<JSONObject> getXiMaAiDetection(@RequestParam String deviceType) {
        String userId = tokenService.getLoginUserApplet().getUserId();
        TSysAppUser sysAppUser = sysAppUserService.getById(userId);
        if(StringUtils.isBlank(sysAppUser.getNickName()) || StringUtils.isBlank(sysAppUser.getPhone()) || Objects.isNull(sysAppUser.getSex()) ||Objects.isNull(sysAppUser.getUserHeight()) || Objects.isNull(sysAppUser.getUserWeight())){
            return R.fail("请先完善用户信息");
        }
        if(StringUtils.isBlank(sysAppUser.getIdentifierNumber())){
            sysAppUser.setIdentifierNumber(UUID.randomUUID().toString().replace("-", ""));
            sysAppUserService.updateById(sysAppUser);
        }
        JSONObject authorizationH5 = ximaAIUtil.getAuthorizationH5(sysAppUser.getIdentifierNumber(), sysAppUser.getNickName(), sysAppUser.getPhone(), sysAppUser.getSex() == 1 ? "0" : "1", sysAppUser.getUserHeight(), sysAppUser.getUserWeight(), deviceType);
        return R.ok(authorizationH5);
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/model/TSysAiConfig.java
@@ -62,5 +62,20 @@
    @TableField("loginPassword")
    private String loginPassword;
    @ApiModelProperty(value = "喜马客户端id")
    @TableField("clientId")
    private String clientId;
    @ApiModelProperty(value = "喜马客户端密钥")
    @TableField("clientSecret")
    private String clientSecret;
    @ApiModelProperty(value = "喜马key")
    @TableField("clientKey")
    private String clientKey;
    @ApiModelProperty(value = "喜马IV")
    @TableField("clientIV")
    private String clientIV;
}
ruoyi-system/src/main/java/com/ruoyi/system/model/TSysAppUser.java
@@ -9,6 +9,7 @@
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -52,10 +53,22 @@
    @TableField("sex")
    private Integer sex;
    @ApiModelProperty(value = "身高cm")
    @TableField("user_height")
    private Integer userHeight;
    @ApiModelProperty(value = "体重kg")
    @TableField("user_weight")
    private Integer userWeight;
    @ApiModelProperty(value = "微信openid")
    @TableField("open_id")
    private String openId;
    @ApiModelProperty(value = "用户AI唯一标识")
    @TableField("identifier_number")
    private String identifierNumber;
    @ApiModelProperty(value = "公众号openId")
    @TableField("office_open_id")
    private String officeOpenId;
ruoyi-system/src/main/java/com/ruoyi/system/utils/util/HttpClientUtils.java
@@ -213,9 +213,10 @@
        try {
            return registRequest(request);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("<<<<<<<<<<<<<<<请求失败:{}", e.getMessage());
            throw new ServiceException("<<<<<<<<<<<http请求失败<<<<<<<<<<<<<<"+e.getMessage());
        }
        return null;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/utils/xiMaUtil/AesUtil.java
New file
@@ -0,0 +1,83 @@
package com.ruoyi.system.utils.xiMaUtil;
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
 *
 * 加密方式:AES CBC模式
 * Key:ciicciicciicciic
 * IV:ciic2022ciic2022
 * 例:
 * 加密前:1234567890
 * 加密后:10ed567b9d42a385926d6166f97008ce
 *
 * 测试地址参考:http://tool.chacuo.net/cryptaes/
 * AES加密模式:CBC  填充:pkcs7padding  数据块:128位 密码:ciicciicciicciic 偏移量:ciic2022ciic2022  输出:hex 字符集:utf8编码(unicode编码)
 *
 */
public class AesUtil {
    static {
        Security.addProvider(new BouncyCastleProvider());
    }
    public static void main(String[] args) {
        try {
            //根据指定字符串生成秘钥
            /*KeyGenerator kg = KeyGenerator.getInstance("AES");
            kg.init(128,new SecureRandom("ceshi".getBytes()));
            SecretKey sk = kg.generateKey();
            String keyStr = Base64.getEncoder().encodeToString(sk.getEncoded());
            byte[] iv = Base64.getDecoder().decode(keyStr);
            byte[] key = Base64.getDecoder().decode(keyStr); */
            String key = "ciicciicciicciic";
            String iv = "ciic2022ciic2022";
            String content = "1234567890";
            String encrypted = encrypt(key, iv,  content);
            System.out.println("加密后: " + encrypted);
            String raw = decrypt(key, iv , encrypted);
            System.out.println("解密后: " + raw);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static String encrypt(String key, String iv,  String content) {
        try {
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");//"算法/模式/补码方式"
            IvParameterSpec ips = new IvParameterSpec(iv.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ips);
            return Hex.byteToHex(cipher.doFinal(content.getBytes()));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public static String decrypt(String key,String iv, String content) {
        try {
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            IvParameterSpec ips = new IvParameterSpec(iv.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, ips);
            return new String(cipher.doFinal(Hex.hexToByte(content)) );
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/utils/xiMaUtil/Hex.java
New file
@@ -0,0 +1,31 @@
package com.ruoyi.system.utils.xiMaUtil;
public class Hex {
    public static String byteToHex(byte[] bytes){
        String strHex = "";
        StringBuilder sb = new StringBuilder("");
        for (int n = 0; n < bytes.length; n++) {
            strHex = Integer.toHexString(bytes[n] & 0xFF);
            sb.append((strHex.length() == 1) ? "0" + strHex : strHex); // 每个字节由两个字符表示,位数不够,高位补0
        }
        return sb.toString().trim();
    }
    public static byte[] hexToByte(String hex){
        int m = 0, n = 0;
        int byteLen = hex.length() / 2; // 每两个字符描述一个字节
        byte[] ret = new byte[byteLen];
        for (int i = 0; i < byteLen; i++) {
            m = i * 2 + 1;
            n = m + 1;
            int intVal = Integer.decode("0x" + hex.substring(i * 2, m) + hex.substring(m, n));
            ret[i] = Byte.valueOf((byte)intVal);
        }
        return ret;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/utils/xiMaUtil/XimaAIUtil.java
New file
@@ -0,0 +1,99 @@
package com.ruoyi.system.utils.xiMaUtil;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.system.model.TSysAiConfig;
import com.ruoyi.system.service.TSysAiConfigService;
import com.ruoyi.system.utils.util.HttpClientUtils;
import com.ruoyi.system.utils.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class XimaAIUtil {
    @Autowired
    private TSysAiConfigService sysAiConfigService;
    /**
     * 获取舌面授权H5页面链接接口
     */
    public static final String AUTHORIZATION_H5_URL_TEST = "http://61.169.68.85:6008/open/web/auth/tongue_face";
    public static final String AUTHORIZATION_H5_URL_PROD = "http://rest.ximalife.com:6008/open/web/auth/tongue_face";
    /**
     * 获取舌面授权H5页面链接接口
     * @param out_user_id 外部用户唯一标志
     * @param nickname 用户昵称,也可用“真实姓名”
     * @param phone     手机号码
     * @param sex   性别(0 男 1 女)
     * @param height 身高(cm)
     * @param weight 体重(kg)
     * @param device_type 设备类型:android ios
     * @return
     */
    public JSONObject getAuthorizationH5(String out_user_id,String nickname,String phone,String sex,Integer height,Integer weight,String device_type) {
        TSysAiConfig sysAiConfig = sysAiConfigService.getById(1);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("client_id", sysAiConfig.getClientId());
        jsonObject.put("client_secret", sysAiConfig.getClientSecret());
        jsonObject.put("out_user_id", out_user_id);
        jsonObject.put("nickname", nickname);
        jsonObject.put("phone", phone);
        jsonObject.put("sex", sex);
        jsonObject.put("height", height);
        jsonObject.put("weight", weight);
        jsonObject.put("device_type", device_type);
        String result = HttpClientUtils.postFrom(AUTHORIZATION_H5_URL_TEST, JsonUtils.toJsonString(jsonObject));
        log.info("获取舌面授权H5页面链接接口:{}", result);
        JSONObject object = JSONObject.parseObject(result);
        if(!object.getBoolean("ok")){
            JSONObject error = object.getJSONObject("error");
            throw new ServiceException(error.getString("err_msg"));
        }
        JSONObject data = object.getJSONObject("data");
        String accessToken = data.getString("access_token");
        Integer expiresIn = data.getInteger("expires_in");
        String requestUrl = data.getString("request_url");
        JSONObject response = new JSONObject();
        response.put("access_token", accessToken);
        response.put("expires_in", expiresIn);
        response.put("request_url", requestUrl);
        log.info("获取舌面授权H5页面返回信息:{}", response);
        return response;
    }
    public static void main(String[] args) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("client_id", "xima_0003");
        jsonObject.put("client_secret", "7987dfafafaf8798dfafadfadff");
        jsonObject.put("out_user_id", "5f68e822-72bb-42cd-ad11-4c2102942dd2");
        jsonObject.put("nickname", "周帅");
        jsonObject.put("phone", "19983174515");
        jsonObject.put("sex", "0");
        jsonObject.put("height", 170);
        jsonObject.put("weight", 65);
        jsonObject.put("device_type", "android");
        String result = HttpClientUtils.postFrom(AUTHORIZATION_H5_URL_TEST, JsonUtils.toJsonString(jsonObject));
        log.info("获取舌面授权H5页面链接接口:{}", result);
        JSONObject object = JSONObject.parseObject(result);
        if(!object.getBoolean("ok")){
            JSONObject error = object.getJSONObject("error");
            throw new ServiceException(error.getString("err_msg"));
        }
        JSONObject data = object.getJSONObject("data");
        String accessToken = data.getString("access_token");
        Integer expiresIn = data.getInteger("expires_in");
        String requestUrl = data.getString("request_url");
        JSONObject response = new JSONObject();
        response.put("access_token", accessToken);
        response.put("expires_in", expiresIn);
        response.put("request_url", requestUrl);
        log.info("获取舌面授权H5页面返回信息:{}", response);
    }
}
ruoyi-system/src/main/resources/mapper/system/TSysAiConfigMapper.xml
@@ -10,11 +10,19 @@
        <result column="devRsaPublicKey" property="devRsaPublicKey" />
        <result column="rsaPublicKey" property="rsaPublicKey" />
        <result column="aesKey" property="aesKey" />
        <result column="devRsaPrivateKey" property="devRsaPrivateKey" />
        <result column="loginName" property="loginName" />
        <result column="loginPassword" property="loginPassword" />
        <result column="clientId" property="clientId" />
        <result column="clientSecret" property="clientSecret" />
        <result column="clientKey" property="clientKey" />
        <result column="clientIV" property="clientIV" />
    </resultMap>
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, devId, devSecret, devRsaPublicKey, rsaPublicKey,aesKey
        id, devId, devSecret, devRsaPublicKey, rsaPublicKey,aesKey,
          devRsaPrivateKey, loginName, loginPassword, clientId, clientSecret, clientKey, clientIV
    </sql>
</mapper>
ruoyi-system/src/main/resources/mapper/system/TSysAppUserMapper.xml
@@ -10,6 +10,9 @@
        <result column="phone" property="phone" />
        <result column="birth_time" property="birthTime" />
        <result column="sex" property="sex" />
        <result column="user_height" property="userHeight" />
        <result column="user_weight" property="userWeight" />
        <result column="identifier_number" property="identifierNumber" />
        <result column="open_id" property="openId" />
        <result column="office_open_id" property="officeOpenId" />
        <result column="chronic_disease_id" property="chronicDiseaseId" />
@@ -25,13 +28,15 @@
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, nick_name,avatar, phone, birth_time, sex, open_id,chronic_disease_id,status,last_reminder_time,
        id, nick_name,avatar, phone, birth_time, sex,user_height, user_weight, identifier_number,
          open_id,chronic_disease_id,status,last_reminder_time,
          office_open_id,clinic_id, create_time, update_time, create_by, update_by, disabled
    </sql>
    <select id="pageList" resultType="com.ruoyi.system.vo.TSysAppUserVO">
        select tsau.id, tsau.nick_name,tsau.avatar, tsau.phone, tsau.birth_time, tsau.sex, tsau.open_id,tsau.office_open_id,
               tsau.chronic_disease_id,tsau.status,tsau.last_reminder_time,tsau.clinic_id, tsau.create_time,
               tsau.update_time, tsau.create_by,tsau.update_by, tsau.disabled, tsi.lastInspectionTime
        select tsau.id, tsau.nick_name,tsau.avatar, tsau.phone, tsau.birth_time, tsau.sex, tsau.user_height,
        tsau.user_weight, tsau.identifier_number,tsau.open_id,tsau.office_open_id,
       tsau.chronic_disease_id,tsau.status,tsau.last_reminder_time,tsau.clinic_id, tsau.create_time,
       tsau.update_time, tsau.create_by,tsau.update_by, tsau.disabled, tsi.lastInspectionTime
        from t_sys_app_user tsau
        left join (select app_user_id, max(check_time) as lastInspectionTime from t_sys_inspection group by app_user_id) tsi on tsi.app_user_id = tsau.id
        <where>
@@ -61,7 +66,8 @@
        ORDER BY tsau.create_time DESC
    </select>
    <select id="pageChronicDiseaseUserList" resultType="com.ruoyi.system.vo.TSysAppUserVO">
        select tsau.id, tsau.nick_name,tsau.avatar, tsau.phone, tsau.birth_time, tsau.sex, tsau.open_id,tsau.office_open_id,
        select tsau.id, tsau.nick_name,tsau.avatar, tsau.phone, tsau.birth_time, tsau.sex, tsau.user_height,
        tsau.user_weight, tsau.identifier_number,tsau.open_id,tsau.office_open_id,
        tsau.chronic_disease_id,tsau.status,tsau.last_reminder_time,tsau.clinic_id, tsau.create_time,
        tsau.update_time, tsau.create_by,tsau.update_by, tsau.disabled
        from t_sys_app_user tsau
@@ -85,7 +91,8 @@
        ORDER BY tsau.create_time DESC
    </select>
    <select id="exportList" resultType="com.ruoyi.system.export.TSysAppUserListExport">
        select tsau.id, tsau.nick_name,tsau.avatar, tsau.phone, tsau.birth_time, tsau.sex, tsau.open_id,tsau.office_open_id,
        select tsau.id, tsau.nick_name,tsau.avatar, tsau.phone, tsau.birth_time, tsau.sex, tsau.user_height,
        tsau.user_weight, tsau.identifier_number,tsau.open_id,tsau.office_open_id,
        tsau.chronic_disease_id,tsau.status,tsau.last_reminder_time,tsau.clinic_id, tsau.create_time,
        tsau.update_time, tsau.create_by,tsau.update_by, tsau.disabled
        from t_sys_app_user tsau
@@ -105,7 +112,8 @@
        ORDER BY tsau.create_time DESC
    </select>
    <select id="exportListClinic" resultType="com.ruoyi.system.export.TSysAppUserClinicExport">
        select tsau.id, tsau.nick_name,tsau.avatar, tsau.phone, tsau.birth_time, tsau.sex, tsau.open_id,tsau.office_open_id,
        select tsau.id, tsau.nick_name,tsau.avatar, tsau.phone, tsau.birth_time, tsau.sex, tsau.user_height,
        tsau.user_weight, tsau.identifier_number,tsau.open_id,tsau.office_open_id,
        tsau.chronic_disease_id,tsau.status,tsau.last_reminder_time,tsau.clinic_id, tsau.create_time,
        tsau.update_time, tsau.create_by,tsau.update_by, tsau.disabled
        from t_sys_app_user tsau
@@ -128,7 +136,8 @@
        ORDER BY tsau.create_time DESC
    </select>
    <select id="exportListAppUser" resultType="com.ruoyi.system.export.TSysAppUserExport">
        select tsau.id, tsau.nick_name,tsau.avatar, tsau.phone, tsau.birth_time, tsau.sex, tsau.open_id,tsau.office_open_id,
        select tsau.id, tsau.nick_name,tsau.avatar, tsau.phone, tsau.birth_time, tsau.sex, tsau.user_height,
        tsau.user_weight, tsau.identifier_number,tsau.open_id,tsau.office_open_id,
        tsau.chronic_disease_id,tsau.status,tsau.last_reminder_time,tsau.clinic_id, tsau.create_time,
        tsau.update_time, tsau.create_by,tsau.update_by, tsau.disabled, tsi.lastInspectionTime
        from t_sys_app_user tsau