Merge remote main: accept deletion of application.properties (replaced by template)

This commit is contained in:
xuan-bot committed 2026-05-07 04:20:24 +08:00
commit d75f9266bb
15 files changed
+398 -208

No files matched your search

+2
View File
@@ -21,3 +21,5 @@ xuan-deduct-uniapp/unpackage/
xuan-deduct-uniapp/.hbuilderx/
.trae/
src/main/resources/application.properties
@@ -0,0 +1,58 @@
package xuan.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "ai")
public class AiProperties {
private String provider = "openai-compatible";
private String apiKey = "";
private String baseUrl = "https://api.openai.com";
private String model = "gpt-3.5-turbo";
private double temperature = 0.7;
private double topP = 0.8;
private int maxTokens = 2048;
private int connectTimeout = 60;
private int readTimeout = 120;
private TencentHunyuan tencentHunyuan = new TencentHunyuan();
public static class TencentHunyuan {
private String secretId = "";
private String secretKey = "";
private String region = "ap-guangzhou";
private String model = "hunyuan-lite";
public String getSecretId() { return secretId; }
public void setSecretId(String secretId) { this.secretId = secretId; }
public String getSecretKey() { return secretKey; }
public void setSecretKey(String secretKey) { this.secretKey = secretKey; }
public String getRegion() { return region; }
public void setRegion(String region) { this.region = region; }
public String getModel() { return model; }
public void setModel(String model) { this.model = model; }
}
public String getProvider() { return provider; }
public void setProvider(String provider) { this.provider = provider; }
public String getApiKey() { return apiKey; }
public void setApiKey(String apiKey) { this.apiKey = apiKey; }
public String getBaseUrl() { return baseUrl; }
public void setBaseUrl(String baseUrl) { this.baseUrl = baseUrl; }
public String getModel() { return model; }
public void setModel(String model) { this.model = model; }
public double getTemperature() { return temperature; }
public void setTemperature(double temperature) { this.temperature = temperature; }
public double getTopP() { return topP; }
public void setTopP(double topP) { this.topP = topP; }
public int getMaxTokens() { return maxTokens; }
public void setMaxTokens(int maxTokens) { this.maxTokens = maxTokens; }
public int getConnectTimeout() { return connectTimeout; }
public void setConnectTimeout(int connectTimeout) { this.connectTimeout = connectTimeout; }
public int getReadTimeout() { return readTimeout; }
public void setReadTimeout(int readTimeout) { this.readTimeout = readTimeout; }
public TencentHunyuan getTencentHunyuan() { return tencentHunyuan; }
public void setTencentHunyuan(TencentHunyuan tencentHunyuan) { this.tencentHunyuan = tencentHunyuan; }
}
+10
View File
@@ -1,11 +1,21 @@
package xuan.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.maxAge(3600);
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
@@ -8,7 +8,7 @@ import xuan.core.bazi.settings.BaZiJiChuSetting;
import xuan.core.bazi.settings.BaZiShenShaSetting;
import xuan.core.bazi.settings.BaZiGanZhiLiuYiSetting;
import xuan.entity.DeductRecord;
import xuan.service.HunYuanAiService;
import xuan.service.AiService;
import xuan.service.PromptTemplates;
import xuan.service.DeductRecordService;
@@ -19,7 +19,8 @@ import java.util.Map;
@RequestMapping("/api/bazi")
public class BaZiController {
private final HunYuanAiService aiService = new HunYuanAiService();
@Autowired
private AiService aiService;
@Autowired
private DeductRecordService recordService;
@@ -1,10 +1,11 @@
package xuan.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import xuan.core.daliuren.DaLiuRen;
import xuan.core.daliuren.settings.DaLiuRenJiChuSetting;
import xuan.core.daliuren.settings.DaLiuRenShenShaSetting;
import xuan.service.HunYuanAiService;
import xuan.service.AiService;
import xuan.service.PromptTemplates;
import java.util.*;
@@ -13,7 +14,8 @@ import java.util.*;
@RequestMapping("/api/daliuren")
public class DaLiuRenController {
private final HunYuanAiService aiService = new HunYuanAiService();
@Autowired
private AiService aiService;
@PostMapping("/paipan")
public Map<String, Object> paiPan(@RequestBody DaLiuRenRequest request) {
@@ -1,10 +1,11 @@
package xuan.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import xuan.core.liuyao.LiuYao;
import xuan.core.liuyao.settings.LiuYaoJiChuSetting;
import xuan.core.liuyao.settings.LiuYaoShenShaSetting;
import xuan.service.HunYuanAiService;
import xuan.service.AiService;
import xuan.service.PromptTemplates;
import java.util.HashMap;
@@ -14,7 +15,8 @@ import java.util.Map;
@RequestMapping("/api/liuyao")
public class LiuYaoController {
private final HunYuanAiService aiService = new HunYuanAiService();
@Autowired
private AiService aiService;
@PostMapping("/paipan")
public Map<String, Object> paiPan(@RequestBody LiuYaoRequest request) {
@@ -1,9 +1,10 @@
package xuan.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import xuan.core.meihua.MeiHua;
import xuan.core.meihua.settings.MeiHuaJiChuSetting;
import xuan.service.HunYuanAiService;
import xuan.service.AiService;
import xuan.service.PromptTemplates;
import java.util.HashMap;
@@ -13,7 +14,8 @@ import java.util.Map;
@RequestMapping("/api/meihua")
public class MeiHuaController {
private final HunYuanAiService aiService = new HunYuanAiService();
@Autowired
private AiService aiService;
@PostMapping("/paipan")
public Map<String, Object> paiPan(@RequestBody MeiHuaRequest request) {
@@ -1,7 +1,8 @@
package xuan.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import xuan.service.HunYuanAiService;
import xuan.service.AiService;
import xuan.service.PromptTemplates;
import java.util.*;
@@ -10,7 +11,8 @@ import java.util.*;
@RequestMapping("/api/naming")
public class NamingController {
private final HunYuanAiService aiService = new HunYuanAiService();
@Autowired
private AiService aiService;
@PostMapping("/generate")
public Map<String, Object> generate(@RequestBody NamingRequest request) {
@@ -1,9 +1,10 @@
package xuan.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import xuan.core.qimen.zhuan.QiMenZhuanPan;
import xuan.core.qimen.zhuan.settings.QiMenZhuanPanJiChuSetting;
import xuan.service.HunYuanAiService;
import xuan.service.AiService;
import xuan.service.PromptTemplates;
import java.util.HashMap;
@@ -13,7 +14,8 @@ import java.util.Map;
@RequestMapping("/api/qimen")
public class QiMenController {
private final HunYuanAiService aiService = new HunYuanAiService();
@Autowired
private AiService aiService;
@PostMapping("/paipan")
public Map<String, Object> paiPan(@RequestBody QiMenRequest request) {
@@ -2,7 +2,7 @@ package xuan.controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import xuan.service.HunYuanAiService;
import xuan.service.AiService;
import xuan.service.PromptTemplates;
import xuan.service.DeductRecordService;
import xuan.entity.DeductRecord;
@@ -14,7 +14,8 @@ import java.util.*;
@RequestMapping("/api/taluo")
public class TaLuoController {
private final HunYuanAiService aiService = new HunYuanAiService();
@Autowired
private AiService aiService;
@Autowired
private DeductRecordService recordService;
@@ -1,7 +1,8 @@
package xuan.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import xuan.service.HunYuanAiService;
import xuan.service.AiService;
import xuan.service.PromptTemplates;
import java.util.*;
@@ -10,7 +11,8 @@ import java.util.*;
@RequestMapping("/api/zhanxing")
public class ZhanXingController {
private final HunYuanAiService aiService = new HunYuanAiService();
@Autowired
private AiService aiService;
@PostMapping("/paipan")
public Map<String, Object> paiPan(@RequestBody ZhanXingRequest request) {
@@ -1,9 +1,10 @@
package xuan.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import xuan.core.ziwei.ZiWei;
import xuan.core.ziwei.settings.ZiWeiJiChuSetting;
import xuan.service.HunYuanAiService;
import xuan.service.AiService;
import xuan.service.PromptTemplates;
import java.util.*;
@@ -12,7 +13,8 @@ import java.util.*;
@RequestMapping("/api/ziwei")
public class ZiWeiController {
private final HunYuanAiService aiService = new HunYuanAiService();
@Autowired
private AiService aiService;
@PostMapping("/paipan")
public Map<String, Object> paiPan(@RequestBody ZiWeiRequest request) {
+272
View File
@@ -0,0 +1,272 @@
package xuan.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import okhttp3.*;
import org.springframework.stereotype.Service;
import xuan.config.AiProperties;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Service
public class AiService {
private final AiProperties properties;
private final OkHttpClient client;
public AiService(AiProperties properties) {
this.properties = properties;
this.client = new OkHttpClient.Builder()
.connectTimeout(properties.getConnectTimeout(), TimeUnit.SECONDS)
.readTimeout(properties.getReadTimeout(), TimeUnit.SECONDS)
.writeTimeout(properties.getConnectTimeout(), TimeUnit.SECONDS)
.build();
}
public String chat(String systemPrompt, String userMessage) {
String provider = properties.getProvider();
if ("tencent-hunyuan".equals(provider)) {
return chatTencentHunyuan(systemPrompt, userMessage);
}
return chatOpenAICompatible(systemPrompt, userMessage);
}
private String chatOpenAICompatible(String systemPrompt, String userMessage) {
String apiKey = properties.getApiKey();
if (apiKey == null || apiKey.isEmpty()) {
return "AI服务未配置,请在application.properties中设置ai.api-key。";
}
try {
String baseUrl = properties.getBaseUrl().replaceAll("/+$", "");
String url = baseUrl + "/v1/chat/completions";
JSONObject requestBody = new JSONObject();
requestBody.put("model", properties.getModel());
JSONArray messages = new JSONArray();
JSONObject systemMsg = new JSONObject();
systemMsg.put("role", "system");
systemMsg.put("content", systemPrompt);
messages.add(systemMsg);
JSONObject userMsg = new JSONObject();
userMsg.put("role", "user");
userMsg.put("content", userMessage);
messages.add(userMsg);
requestBody.put("messages", messages);
requestBody.put("temperature", properties.getTemperature());
requestBody.put("top_p", properties.getTopP());
requestBody.put("max_tokens", properties.getMaxTokens());
String payload = requestBody.toJSONString();
Request request = new Request.Builder()
.url(url)
.post(RequestBody.create(payload, MediaType.parse("application/json; charset=utf-8")))
.addHeader("Authorization", "Bearer " + apiKey)
.addHeader("Content-Type", "application/json; charset=utf-8")
.build();
try (Response response = client.newCall(request).execute()) {
String responseBody = response.body() != null ? response.body().string() : "";
if (!response.isSuccessful()) {
return "AI服务请求失败(HTTP " + response.code() + "):" + responseBody;
}
return parseOpenAIResponse(responseBody);
}
} catch (Exception e) {
e.printStackTrace();
return "AI分析暂时不可用,请稍后再试。错误信息:" + e.getMessage();
}
}
private String parseOpenAIResponse(String responseBody) {
try {
JSONObject json = JSON.parseObject(responseBody);
JSONArray choices = json.getJSONArray("choices");
if (choices != null && !choices.isEmpty()) {
JSONObject choice = choices.getJSONObject(0);
JSONObject message = choice.getJSONObject("message");
if (message != null) {
return message.getString("content");
}
}
return "AI未返回有效内容";
} catch (Exception e) {
return "解析AI响应失败:" + e.getMessage();
}
}
private String chatTencentHunyuan(String systemPrompt, String userMessage) {
AiProperties.TencentHunyuan hy = properties.getTencentHunyuan();
String secretId = hy.getSecretId();
String secretKey = hy.getSecretKey();
if (secretId == null || secretId.isEmpty() || secretKey == null || secretKey.isEmpty()) {
return "腾讯混元AI服务未配置,请设置ai.tencent-hunyuan.secret-id和ai.tencent-hunyuan.secret-key。";
}
try {
String response = sendTencentHunyuanRequest(systemPrompt, userMessage, secretId, secretKey, hy);
return parseTencentHunyuanResponse(response);
} catch (Exception e) {
e.printStackTrace();
return "AI分析暂时不可用,请稍后再试。错误信息:" + e.getMessage();
}
}
private String sendTencentHunyuanRequest(String systemPrompt, String userMessage,
String secretId, String secretKey,
AiProperties.TencentHunyuan hy) throws IOException {
String host = "hunyuan.tencentcloudapi.com";
String service = "hunyuan";
String action = "ChatCompletions";
String version = "2023-09-01";
String region = hy.getRegion();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date now = new Date();
String date = sdf.format(now);
String timestamp = String.valueOf(now.getTime() / 1000);
JSONObject requestBody = new JSONObject();
requestBody.put("Model", hy.getModel());
JSONArray messages = new JSONArray();
JSONObject systemMsg = new JSONObject();
systemMsg.put("Role", "system");
systemMsg.put("Content", systemPrompt);
messages.add(systemMsg);
JSONObject userMsg = new JSONObject();
userMsg.put("Role", "user");
userMsg.put("Content", userMessage);
messages.add(userMsg);
requestBody.put("Messages", messages);
requestBody.put("Temperature", properties.getTemperature());
requestBody.put("TopP", properties.getTopP());
String payload = requestBody.toJSONString();
String authorization = generateTc3Authorization(secretId, secretKey, timestamp, date, payload, host, service);
Request request = new Request.Builder()
.url("https://" + host)
.post(RequestBody.create(payload, MediaType.parse("application/json; charset=utf-8")))
.addHeader("Authorization", authorization)
.addHeader("Content-Type", "application/json; charset=utf-8")
.addHeader("Host", host)
.addHeader("X-TC-Action", action)
.addHeader("X-TC-Version", version)
.addHeader("X-TC-Timestamp", timestamp)
.addHeader("X-TC-Region", region)
.build();
try (Response response = client.newCall(request).execute()) {
String responseBody = response.body() != null ? response.body().string() : "";
if (!response.isSuccessful()) {
return "{\"Response\":{\"Error\":{\"Message\":\"HTTP " + response.code() + ": " + responseBody + "\"}}}";
}
return responseBody;
}
}
private String generateTc3Authorization(String secretId, String secretKey, String timestamp,
String date, String payload, String host, String service) {
try {
String canonicalUri = "/";
String canonicalQueryString = "";
String canonicalHeaders = "content-type:application/json; charset=utf-8\nhost:" + host + "\n";
String signedHeaders = "content-type;host";
String hashedRequestPayload = sha256Hex(payload);
String canonicalRequest = "POST\n" +
canonicalUri + "\n" +
canonicalQueryString + "\n" +
canonicalHeaders + "\n" +
signedHeaders + "\n" +
hashedRequestPayload;
String algorithm = "TC3-HMAC-SHA256";
String credentialScope = date + "/" + service + "/tc3_request";
String hashedCanonicalRequest = sha256Hex(canonicalRequest);
String stringToSign = algorithm + "\n" +
timestamp + "\n" +
credentialScope + "\n" +
hashedCanonicalRequest;
byte[] secretDate = hmacSha256(("TC3" + secretKey).getBytes(StandardCharsets.UTF_8), date);
byte[] secretService = hmacSha256(secretDate, service);
byte[] secretSigning = hmacSha256(secretService, "tc3_request");
String signature = bytesToHex(hmacSha256(secretSigning, stringToSign));
return algorithm + " " +
"Credential=" + secretId + "/" + credentialScope + ", " +
"SignedHeaders=" + signedHeaders + ", " +
"Signature=" + signature;
} catch (Exception e) {
throw new RuntimeException("Failed to generate authorization", e);
}
}
private String parseTencentHunyuanResponse(String response) {
try {
JSONObject jsonResponse = JSON.parseObject(response);
JSONObject responseObj = jsonResponse.getJSONObject("Response");
if (responseObj.containsKey("Error")) {
JSONObject error = responseObj.getJSONObject("Error");
return "AI服务错误:" + error.getString("Message");
}
JSONArray choices = responseObj.getJSONArray("Choices");
if (choices != null && !choices.isEmpty()) {
JSONObject choice = choices.getJSONObject(0);
JSONObject message = choice.getJSONObject("Message");
return message.getString("Content");
}
return "AI未返回有效内容";
} catch (Exception e) {
return "解析AI响应失败:" + e.getMessage();
}
}
private static String sha256Hex(String input) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
return bytesToHex(hash);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static byte[] hmacSha256(byte[] key, String message) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(key, "HmacSHA256"));
return mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}
@@ -1,186 +0,0 @@
package xuan.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import okhttp3.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
public class HunYuanAiService {
private static final String SECRET_ID = System.getenv("TENCENT_SECRET_ID");
private static final String SECRET_KEY = System.getenv("TENCENT_SECRET_KEY");
private static final String SERVICE = "hunyuan";
private static final String HOST = "hunyuan.tencentcloudapi.com";
private static final String REGION = "ap-guangzhou";
private static final String ACTION = "ChatCompletions";
private static final String VERSION = "2023-09-01";
private static final OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.build();
public String chat(String systemPrompt, String userMessage) {
if (SECRET_ID == null || SECRET_KEY == null) {
return "AI服务未配置,请设置TENCENT_SECRET_ID和TENCENT_SECRET_KEY环境变量。";
}
try {
String response = sendRequest(systemPrompt, userMessage);
return parseResponse(response);
} catch (Exception e) {
e.printStackTrace();
return "AI分析暂时不可用,请稍后再试。错误信息:" + e.getMessage();
}
}
private String sendRequest(String systemPrompt, String userMessage) throws IOException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date now = new Date();
String date = sdf.format(now);
String timestamp = String.valueOf(now.getTime() / 1000);
JSONObject requestBody = new JSONObject();
requestBody.put("Model", "hunyuan-lite");
JSONArray messages = new JSONArray();
JSONObject systemMsg = new JSONObject();
systemMsg.put("Role", "system");
systemMsg.put("Content", systemPrompt);
messages.add(systemMsg);
JSONObject userMsg = new JSONObject();
userMsg.put("Role", "user");
userMsg.put("Content", userMessage);
messages.add(userMsg);
requestBody.put("Messages", messages);
requestBody.put("Temperature", 0.7);
requestBody.put("TopP", 0.8);
String payload = requestBody.toJSONString();
String authorization = generateAuthorization(timestamp, date, payload);
Request request = new Request.Builder()
.url("https://" + HOST)
.post(RequestBody.create(payload, MediaType.parse("application/json; charset=utf-8")))
.addHeader("Authorization", authorization)
.addHeader("Content-Type", "application/json; charset=utf-8")
.addHeader("Host", HOST)
.addHeader("X-TC-Action", ACTION)
.addHeader("X-TC-Version", VERSION)
.addHeader("X-TC-Timestamp", timestamp)
.addHeader("X-TC-Region", REGION)
.build();
try (Response response = client.newCall(request).execute()) {
String responseBody = response.body() != null ? response.body().string() : "";
if (!response.isSuccessful()) {
return "{\"Response\":{\"Error\":{\"Message\":\"HTTP " + response.code() + ": " + responseBody + "\"}}}";
}
return responseBody;
}
}
private String generateAuthorization(String timestamp, String date, String payload) {
try {
String httpRequestMethod = "POST";
String canonicalUri = "/";
String canonicalQueryString = "";
String canonicalHeaders = "content-type:application/json; charset=utf-8\nhost:" + HOST + "\n";
String signedHeaders = "content-type;host";
String hashedRequestPayload = sha256Hex(payload);
String canonicalRequest = httpRequestMethod + "\n" +
canonicalUri + "\n" +
canonicalQueryString + "\n" +
canonicalHeaders + "\n" +
signedHeaders + "\n" +
hashedRequestPayload;
String algorithm = "TC3-HMAC-SHA256";
String credentialScope = date + "/" + SERVICE + "/tc3_request";
String hashedCanonicalRequest = sha256Hex(canonicalRequest);
String stringToSign = algorithm + "\n" +
timestamp + "\n" +
credentialScope + "\n" +
hashedCanonicalRequest;
byte[] secretDate = hmacSha256(("TC3" + SECRET_KEY).getBytes(StandardCharsets.UTF_8), date);
byte[] secretService = hmacSha256(secretDate, SERVICE);
byte[] secretSigning = hmacSha256(secretService, "tc3_request");
String signature = bytesToHex(hmacSha256(secretSigning, stringToSign));
return algorithm + " " +
"Credential=" + SECRET_ID + "/" + credentialScope + ", " +
"SignedHeaders=" + signedHeaders + ", " +
"Signature=" + signature;
} catch (Exception e) {
throw new RuntimeException("Failed to generate authorization", e);
}
}
private String parseResponse(String response) {
try {
JSONObject jsonResponse = JSON.parseObject(response);
JSONObject responseObj = jsonResponse.getJSONObject("Response");
if (responseObj.containsKey("Error")) {
JSONObject error = responseObj.getJSONObject("Error");
return "AI服务错误:" + error.getString("Message");
}
JSONArray choices = responseObj.getJSONArray("Choices");
if (choices != null && !choices.isEmpty()) {
JSONObject choice = choices.getJSONObject(0);
JSONObject message = choice.getJSONObject("Message");
return message.getString("Content");
}
return "AI未返回有效内容";
} catch (Exception e) {
return "解析AI响应失败:" + e.getMessage();
}
}
private static String sha256Hex(String input) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
return bytesToHex(hash);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static byte[] hmacSha256(byte[] key, String message) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(key, "HmacSHA256"));
return mac.doFinal(message.getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}
@@ -4,8 +4,8 @@ spring.application.name=xuan-utils-pro
# MySQL Database Configuration
spring.datasource.url=jdbc:mysql://localhost:3306/xuan_deduct?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8&allowPublicKeyRetrieval=true
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.username=xuan_user
spring.datasource.password=xuan_password
spring.datasource.username=YOUR_DB_USERNAME
spring.datasource.password=YOUR_DB_PASSWORD
# JPA Configuration
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
@@ -17,10 +17,28 @@ spring.jpa.properties.hibernate.format_sql=true
logging.level.xuan=DEBUG
# WeChat Mini Program Configuration
wechat.appid=${WECHAT_APPID:}
wechat.secret=${WECHAT_SECRET:}
wechat.appid=${WECHAT_APPID:YOUR_WECHAT_APPID}
wechat.secret=${WECHAT_SECRET:YOUR_WECHAT_SECRET}
# WeChat H5 QR Login Configuration
wechat.web.appid=${WECHAT_WEB_APPID:${WECHAT_APPID:}}
wechat.web.secret=${WECHAT_WEB_SECRET:${WECHAT_SECRET:}}
wechat.web.callback-base-url=${WECHAT_WEB_CALLBACK_BASE_URL:https://api.bemover.xyz}
# AI Configuration
# Provider: openai-compatible | tencent-hunyuan
ai.provider=${AI_PROVIDER:openai-compatible}
ai.api-key=${AI_API_KEY:YOUR_AI_API_KEY}
ai.base-url=${AI_BASE_URL:https://api.openai.com}
ai.model=${AI_MODEL:gpt-3.5-turbo}
ai.temperature=0.7
ai.top-p=0.8
ai.max-tokens=2048
ai.connect-timeout=60
ai.read-timeout=120
# Tencent Hunyuan (used when ai.provider=tencent-hunyuan)
ai.tencent-hunyuan.secret-id=${TENCENT_SECRET_ID:YOUR_TENCENT_SECRET_ID}
ai.tencent-hunyuan.secret-key=${TENCENT_SECRET_KEY:YOUR_TENCENT_SECRET_KEY}
ai.tencent-hunyuan.region=ap-guangzhou
ai.tencent-hunyuan.model=hunyuan-lite