|
@@ -0,0 +1,164 @@
|
|
|
|
|
+package shop.alien.lawyer.util.ai;
|
|
|
|
|
+
|
|
|
|
|
+import com.alibaba.fastjson2.JSONObject;
|
|
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
|
|
+import org.springframework.cloud.context.config.annotation.RefreshScope;
|
|
|
|
|
+import org.springframework.http.HttpEntity;
|
|
|
|
|
+import org.springframework.http.HttpHeaders;
|
|
|
|
|
+import org.springframework.http.HttpStatus;
|
|
|
|
|
+import org.springframework.http.MediaType;
|
|
|
|
|
+import org.springframework.http.ResponseEntity;
|
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
|
+import org.springframework.util.StringUtils;
|
|
|
|
|
+import org.springframework.web.client.RestTemplate;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.Objects;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 律师执业证/资格证 AI 核验(入驻时调用)
|
|
|
|
|
+ */
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+@Component
|
|
|
|
|
+@RefreshScope
|
|
|
|
|
+@RequiredArgsConstructor
|
|
|
|
|
+public class LawyerLicenseVerifyUtil {
|
|
|
|
|
+
|
|
|
|
|
+ private final RestTemplate restTemplate;
|
|
|
|
|
+ private final AiAuthTokenUtil aiAuthTokenUtil;
|
|
|
|
|
+
|
|
|
|
|
+ @Value("${ai.service.lawyer-license-verify-url:http://124.93.18.180:9000/ai/multimodal-services/api/v1/lawyer-license/verify}")
|
|
|
|
|
+ private String lawyerLicenseVerifyUrl;
|
|
|
|
|
+
|
|
|
|
|
+ public static class VerifyResult {
|
|
|
|
|
+ private final boolean passed;
|
|
|
|
|
+ private final String message;
|
|
|
|
|
+
|
|
|
|
|
+ public VerifyResult(boolean passed, String message) {
|
|
|
|
|
+ this.passed = passed;
|
|
|
|
|
+ this.message = message;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public boolean isPassed() {
|
|
|
|
|
+ return passed;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public String getMessage() {
|
|
|
|
|
+ return message;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 核验执业证照片、姓名与头像是否与证件一致
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param certificateImageUrl 执业证照片 URL
|
|
|
|
|
+ * @param realName 待核验姓名
|
|
|
|
|
+ * @param userAvatarUrl 律师头像 URL
|
|
|
|
|
+ */
|
|
|
|
|
+ public VerifyResult verify(String certificateImageUrl, String realName, String userAvatarUrl) {
|
|
|
|
|
+ if (!StringUtils.hasText(certificateImageUrl) || !StringUtils.hasText(realName) || !StringUtils.hasText(userAvatarUrl)) {
|
|
|
|
|
+ return new VerifyResult(false, "请完整上传律师执业证照片、本人头像,并填写与证件一致的真实姓名。");
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ String accessToken = aiAuthTokenUtil.getAccessToken();
|
|
|
|
|
+ if (!StringUtils.hasText(accessToken)) {
|
|
|
|
|
+ log.error("律师执业证核验失败:无法获取 AI 服务访问令牌");
|
|
|
|
|
+ return new VerifyResult(false, "执业证核验服务暂不可用,请稍后重试。如多次失败请联系平台客服。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ HttpHeaders headers = new HttpHeaders();
|
|
|
|
|
+ headers.setContentType(MediaType.APPLICATION_JSON);
|
|
|
|
|
+ headers.set("Authorization", "Bearer " + accessToken);
|
|
|
|
|
+
|
|
|
|
|
+ JSONObject body = new JSONObject();
|
|
|
|
|
+ body.put("image_url", certificateImageUrl.trim());
|
|
|
|
|
+ body.put("text", realName.trim());
|
|
|
|
|
+ body.put("user_avatar_url", userAvatarUrl.trim());
|
|
|
|
|
+
|
|
|
|
|
+ String json = Objects.requireNonNull(body.toJSONString());
|
|
|
|
|
+ HttpEntity<String> entity = new HttpEntity<>(json, headers);
|
|
|
|
|
+
|
|
|
|
|
+ log.info("调用律师执业证核验接口:url={}", lawyerLicenseVerifyUrl);
|
|
|
|
|
+ ResponseEntity<String> response = restTemplate.postForEntity(lawyerLicenseVerifyUrl, entity, String.class);
|
|
|
|
|
+
|
|
|
|
|
+ if (response.getStatusCode() != HttpStatus.OK || !StringUtils.hasText(response.getBody())) {
|
|
|
|
|
+ log.error("律师执业证核验 HTTP 异常:status={}", response.getStatusCode());
|
|
|
|
|
+ return new VerifyResult(false, "执业证核验服务响应异常,请稍后重试。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ JSONObject root = JSONObject.parseObject(response.getBody());
|
|
|
|
|
+ return parseResult(root);
|
|
|
|
|
+
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("律师执业证核验调用异常", e);
|
|
|
|
|
+ return new VerifyResult(false, "执业证核验服务暂时不可用,请稍后重试。");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private VerifyResult parseResult(JSONObject root) {
|
|
|
|
|
+ Integer code = root.getInteger("code");
|
|
|
|
|
+ if (code == null || code != 0) {
|
|
|
|
|
+ String msg = root.getString("message");
|
|
|
|
|
+ log.warn("律师执业证核验接口业务错误:code={}, message={}", code, msg);
|
|
|
|
|
+ if (code != null && code == 422) {
|
|
|
|
|
+ return new VerifyResult(false, "提交资料不完整或格式有误,请检查执业证照片、头像与姓名后重试。");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (StringUtils.hasText(msg)) {
|
|
|
|
|
+ return new VerifyResult(false, "执业证核验未通过:" + msg);
|
|
|
|
|
+ }
|
|
|
|
|
+ return new VerifyResult(false, "执业证核验未通过,请稍后重试。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ JSONObject data = root.getJSONObject("data");
|
|
|
|
|
+ if (data == null) {
|
|
|
|
|
+ log.warn("律师执业证核验返回 data 为空");
|
|
|
|
|
+ return new VerifyResult(false, "执业证核验结果异常,请稍后重试。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (isPass(data)) {
|
|
|
|
|
+ log.info("律师执业证核验通过");
|
|
|
|
|
+ return new VerifyResult(true, null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String userMessage = buildUserFacingFailure(data);
|
|
|
|
|
+ log.warn("律师执业证核验未通过:{}", userMessage);
|
|
|
|
|
+ return new VerifyResult(false, userMessage);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static boolean isPass(JSONObject data) {
|
|
|
|
|
+ Integer i = data.getInteger("is_pass");
|
|
|
|
|
+ if (i != null) {
|
|
|
|
|
+ return i == 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ return Boolean.TRUE.equals(data.getBoolean("is_pass"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 面向用户的失败说明(结合接口 reason 与分项标志)
|
|
|
|
|
+ */
|
|
|
|
|
+ private String buildUserFacingFailure(JSONObject data) {
|
|
|
|
|
+ String reason = data.getString("reason");
|
|
|
|
|
+ Boolean isLawyerLicense = data.getBoolean("is_lawyer_license");
|
|
|
|
|
+ Boolean nameMatch = data.getBoolean("name_match");
|
|
|
|
|
+ Boolean faceMatch = data.getBoolean("face_match");
|
|
|
|
|
+
|
|
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
|
|
+ sb.append("律师执业证核验未通过。");
|
|
|
|
|
+
|
|
|
|
|
+ if (Boolean.FALSE.equals(isLawyerLicense)) {
|
|
|
|
|
+ sb.append("上传图片未能识别为有效的律师执业证或律师资格证,请拍摄证件原件清晰、四角完整的照片后重新上传。");
|
|
|
|
|
+ } else if (Boolean.FALSE.equals(nameMatch)) {
|
|
|
|
|
+ sb.append("证件所载姓名与您填写的姓名不一致,请填写与证件完全一致的真实姓名。");
|
|
|
|
|
+ } else if (Boolean.FALSE.equals(faceMatch)) {
|
|
|
|
|
+ sb.append("本人头像与证件照人像不一致,请使用本人近期清晰正面头像。");
|
|
|
|
|
+ } else {
|
|
|
|
|
+ sb.append("请确保证件信息清晰可辨,且姓名、头像与执业证记载一致。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (StringUtils.hasText(reason)) {
|
|
|
|
|
+ sb.append("(").append(reason.trim()).append(")");
|
|
|
|
|
+ }
|
|
|
|
|
+ return sb.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|