ErpClient.java 4.93 KB
package com.xly.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.List;
import java.util.Map;

/**
 * ERP 后端(xlyEntry)薄 HTTP 客户端。
 *
 * <p>xlyAi 的读写业务逻辑在 ERP 后端,xlyAi 只是像前端一样发同样的 API 请求。本类负责:
 * <ul>
 *   <li>取得可用的 ERP 会话 token —— 本地开发用配置的 dev 账号 {@code /checklogin} 登录并缓存;
 *       生产应改为透传用户浏览器里的 ERP 登录 token(per-request,never stored)。</li>
 *   <li>调用通用表单读接口 {@code getBusinessDataByFormcustomId};会话过期(code=-2)自动重登一次重试。</li>
 * </ul>
 */
@Service
public class ErpClient {

    private static final Logger log = LoggerFactory.getLogger(ErpClient.class);

    private final ObjectMapper mapper;
    private final HttpClient http = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();

    @Value("${erp.baseurl}")
    private String baseUrl;
    @Value("${erp.dev-login.brand:1111111111}")
    private String brand;
    @Value("${erp.dev-login.subsidiary:1111111111}")
    private String subsidiary;
    @Value("${erp.dev-login.username:admin}")
    private String username;
    @Value("${erp.dev-login.password:666666}")
    private String password;

    private volatile String cachedToken;

    public ErpClient(ObjectMapper mapper) {
        this.mapper = mapper;
    }

    /** 用配置的 dev 账号登录 ERP,缓存返回的 Authorization token。 */
    private synchronized String login() {
        try {
            String url = baseUrl + "/checklogin/" + brand + "/" + subsidiary;
            String body = mapper.writeValueAsString(Map.of("username", username, "password", password));
            HttpRequest req = HttpRequest.newBuilder(URI.create(url))
                    .header("Content-Type", "application/json;charset=UTF-8")
                    .timeout(Duration.ofSeconds(20))
                    .POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8))
                    .build();
            HttpResponse<String> resp = http.send(req, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
            JsonNode root = mapper.readTree(resp.body());
            if (root.path("code").asInt() != 1) {
                throw new IllegalStateException("ERP 登录失败: " + root.path("msg").asText());
            }
            String token = root.path("dataset").path("rows").path(0).path("token").asText(null);
            if (token == null || token.isBlank()) {
                throw new IllegalStateException("ERP 登录未返回 token");
            }
            cachedToken = token;
            log.info("ERP dev-login ok (user={}), token cached", username);
            return token;
        } catch (Exception e) {
            throw new RuntimeException("ERP 登录异常: " + e.getMessage(), e);
        }
    }

    private String token() {
        String t = cachedToken;
        return (t != null && !t.isBlank()) ? t : login();
    }

    /**
     * 读取某表单一页数据,返回整个响应根节点(含 code / msg / dataset)。
     * 会话过期(code=-2)时自动重登一次并重试。
     */
    public JsonNode readForm(String formId, String moduleId, int page, int pageSize) {
        JsonNode root = doRead(formId, moduleId, page, pageSize, token());
        if (root.path("code").asInt() == -2) {
            login();
            root = doRead(formId, moduleId, page, pageSize, token());
        }
        return root;
    }

    private JsonNode doRead(String formId, String moduleId, int page, int pageSize, String tok) {
        try {
            String url = baseUrl + "/business/getBusinessDataByFormcustomId/" + formId
                    + "?sModelsId=" + moduleId + "&sName=";
            String body = mapper.writeValueAsString(Map.of(
                    "pageNum", page, "pageSize", pageSize, "bFilter", List.of()));
            HttpRequest req = HttpRequest.newBuilder(URI.create(url))
                    .header("Content-Type", "application/json;charset=UTF-8")
                    .header("Authorization", tok)
                    .timeout(Duration.ofSeconds(30))
                    .POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8))
                    .build();
            HttpResponse<String> resp = http.send(req, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
            return mapper.readTree(resp.body());
        } catch (Exception e) {
            throw new RuntimeException("ERP 读取异常: " + e.getMessage(), e);
        }
    }
}