Compare commits

...

12 Commits

7 changed files with 260 additions and 17 deletions

View File

@ -20,6 +20,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Locale; import java.util.Locale;
/** /**
@ -336,12 +337,16 @@ public class TimiSpring {
} }
/** /**
* 获取请求头的令牌,键为 Token * 获取请求令牌,键为 Token 或 token包括请求头和 URI
* *
* @return 令牌 * @return 令牌
*/ */
public static String getToken() { public static String getToken() {
return getHeader("Token"); return TimiJava.firstNotEmpty(getHeader("Token"), getHeader("token"), getRequestArg("token"), getRequestArg("Token"));
}
public static String getLanguageRaw() {
return getHeader("Accept-Language");
} }
/** /**
@ -349,9 +354,18 @@ public class TimiSpring {
* @return 客户端地区语言 * @return 客户端地区语言
*/ */
public static Language getLanguage() { public static Language getLanguage() {
String name = TimiSpring.getHeader("Language"); String name = getRequestArg("lang");
if (TimiJava.isEmpty(name)) { if (TimiJava.isEmpty(name)) {
name = TimiSpring.getLocale().toString(); List<Locale.LanguageRange> rangeList = Locale.LanguageRange.parse(getLanguageRaw());
for (Locale.LanguageRange item : rangeList) {
if (item.getRange().contains("-")) {
name = item.getRange();
break;
}
}
}
if (TimiJava.isNotEmpty(name)) {
name = name.replace("-", "_");
} }
if (TimiJava.isEmpty(name)) { // use for not support if (TimiJava.isEmpty(name)) { // use for not support
return Language.zh_CN; return Language.zh_CN;

View File

@ -0,0 +1,105 @@
package com.imyeyu.spring.bean;
import com.imyeyu.java.ref.Ref;
import com.imyeyu.spring.entity.UUIDEntity;
/**
* @author 夜雨
* @since 2025-10-17 15:21
*/
public class Multilingual extends UUIDEntity {
protected String key;
protected String zhCN;
protected String zhTW;
protected String enUS;
protected String ruRU;
protected String koKR;
protected String jaJP;
protected String deDE;
/**
* 获取指定语言值
*
* @param language 指定语言
* @return 值
*/
public String getValue(com.imyeyu.java.bean.Language language) {
try {
return Ref.getFieldValue(this, language.toString().replace("_", ""), String.class);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getZhCN() {
return zhCN;
}
public void setZhCN(String zhCN) {
this.zhCN = zhCN;
}
public String getZhTW() {
return zhTW;
}
public void setZhTW(String zhTW) {
this.zhTW = zhTW;
}
public String getEnUS() {
return enUS;
}
public void setEnUS(String enUS) {
this.enUS = enUS;
}
public String getRuRU() {
return ruRU;
}
public void setRuRU(String ruRU) {
this.ruRU = ruRU;
}
public String getKoKR() {
return koKR;
}
public void setKoKR(String koKR) {
this.koKR = koKR;
}
public String getJaJP() {
return jaJP;
}
public void setJaJP(String jaJP) {
this.jaJP = jaJP;
}
public String getDeDE() {
return deDE;
}
public void setDeDE(String deDE) {
this.deDE = deDE;
}
}

View File

@ -0,0 +1,57 @@
package com.imyeyu.spring.handler;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.imyeyu.java.TimiJava;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* MySQL JSON 数据类型处理器
*
* @author 夜雨
* @since 2021-07-04 09:36
*/
public class GsonHandler extends BaseTypeHandler<JsonElement> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, JsonElement parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter.toString());
}
@Override
public JsonElement getNullableResult(ResultSet rs, String columnName) throws SQLException {
return toElement(rs.getString(columnName));
}
@Override
public JsonElement getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return toElement(rs.getString(columnIndex));
}
@Override
public JsonElement getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return toElement(cs.getNString(columnIndex));
}
private JsonElement toElement(String json) {
if (TimiJava.isNotEmpty(json)) {
JsonElement el = JsonParser.parseString(json);
if (el.isJsonObject()) {
return el.getAsJsonObject();
}
if (el.isJsonArray()) {
return el.getAsJsonArray();
}
if (el.isJsonPrimitive()) {
return el.getAsJsonPrimitive();
}
}
return null;
}
}

View File

@ -56,7 +56,7 @@ public class GlobalExceptionHandler {
if (env.contains("dev") || log.isDebugEnabled()) { if (env.contains("dev") || log.isDebugEnabled()) {
log.error("header error", e); log.error("header error", e);
} }
return new TimiResponse<>(TimiCode.REQUEST_BAD).msgKey("invalid.header"); return new TimiResponse<>(TimiCode.REQUEST_BAD).msgKey("invalid.request");
} }
/** /**
@ -71,7 +71,7 @@ public class GlobalExceptionHandler {
log.warn("request error", e); log.warn("request error", e);
FieldError error = subE.getBindingResult().getFieldError(); FieldError error = subE.getBindingResult().getFieldError();
if (error != null) { if (error != null) {
return new TimiResponse<>(TimiCode.ARG_BAD, error.getDefaultMessage()); return new TimiResponse<>(TimiCode.ARG_BAD, "[%s] %s".formatted(error.getField(), error.getDefaultMessage()));
} }
} }
if (env.startsWith("dev") || log.isDebugEnabled()) { if (env.startsWith("dev") || log.isDebugEnabled()) {
@ -89,7 +89,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(Throwable.class) @ExceptionHandler(Throwable.class)
public TimiResponse<?> error(Throwable e) { public TimiResponse<?> error(Throwable e) {
if (e instanceof TimiException timiE) { if (e instanceof TimiException timiE) {
if (env.startsWith("dev") || log.isDebugEnabled()) { if (!env.startsWith("prod") || log.isDebugEnabled()) {
log.error(timiE.getMessage(), e); log.error(timiE.getMessage(), e);
} }
// 一般异常 // 一般异常

View File

@ -2,6 +2,7 @@ package com.imyeyu.spring.util;
import com.imyeyu.java.TimiJava; import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.CallbackArgReturn; import com.imyeyu.java.bean.CallbackArgReturn;
import com.imyeyu.java.bean.LanguageMsgMapping;
import com.imyeyu.java.bean.timi.TimiCode; import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiResponse; import com.imyeyu.java.bean.timi.TimiResponse;
import com.imyeyu.spring.TimiSpring; import com.imyeyu.spring.TimiSpring;
@ -31,7 +32,7 @@ public class GlobalReturnHandler implements ResponseBodyAdvice<Object> {
private static final Logger log = LoggerFactory.getLogger(GlobalReturnHandler.class); private static final Logger log = LoggerFactory.getLogger(GlobalReturnHandler.class);
private CallbackArgReturn<String, String> multilingualHeader; private CallbackArgReturn<LanguageMsgMapping<?>, String> multilingualHeader;
@Override @Override
public boolean supports(@NonNull MethodParameter returnType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) { public boolean supports(@NonNull MethodParameter returnType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
@ -54,22 +55,27 @@ public class GlobalReturnHandler implements ResponseBodyAdvice<Object> {
} else { } else {
result = new TimiResponse<>(TimiCode.SUCCESS, body); result = new TimiResponse<>(TimiCode.SUCCESS, body);
} }
try {
if (multilingualHeader != null && TimiJava.isNotEmpty(result.getMsgKey())) { if (multilingualHeader != null && TimiJava.isNotEmpty(result.getMsgKey())) {
result.setMsg(multilingualHeader.handler(result.getMsgKey())); result.setMsg(multilingualHeader.handler(result));
} else if (TimiJava.isEmpty(result.getMsg())) { } else if (TimiJava.isEmpty(result.getMsg())) {
result.setMsg(TimiCode.fromCode(result.getCode()).toString()); result.setMsg(TimiCode.fromCode(result.getCode()).toString());
} }
} catch (Exception e) {
log.error("multilingual response error", e);
result.setMsg(TimiCode.fromCode(result.getCode()).toString());
}
if (30000 < result.getCode()) { if (30000 < result.getCode()) {
log.warn("ID: {} Response -> Exception.{}.{}", TimiSpring.getSessionAttr(AOPLogInterceptor.REQUEST_ID), result.getCode(), result.getMsg()); log.warn("ID: {} Response -> Exception.{}.{}", TimiSpring.getSessionAttr(AOPLogInterceptor.REQUEST_ID), result.getCode(), result.getMsg());
} }
return result; return result;
} }
public CallbackArgReturn<String, String> getMultilingualHeader() { public CallbackArgReturn<LanguageMsgMapping<?>, String> getMultilingualHeader() {
return multilingualHeader; return multilingualHeader;
} }
public void setMultilingualHeader(CallbackArgReturn<String, String> multilingualHeader) { public void setMultilingualHeader(CallbackArgReturn<LanguageMsgMapping<?>, String> multilingualHeader) {
this.multilingualHeader = multilingualHeader; this.multilingualHeader = multilingualHeader;
} }
} }

View File

@ -272,7 +272,7 @@ public class SQLProvider {
* @param context 代理器上下文 * @param context 代理器上下文
* @return 实体类元数据 * @return 实体类元数据
*/ */
private EntityMeta getEntityMeta(ProviderContext context) { protected EntityMeta getEntityMeta(ProviderContext context) {
Type[] types = context.getMapperType().getGenericInterfaces(); Type[] types = context.getMapperType().getGenericInterfaces();
ParameterizedType type = (ParameterizedType) types[0]; ParameterizedType type = (ParameterizedType) types[0];
Class<?> entityClass = (Class<?>) type.getActualTypeArguments()[0]; Class<?> entityClass = (Class<?>) type.getActualTypeArguments()[0];
@ -285,7 +285,7 @@ public class SQLProvider {
* @param entityClass 实体类 * @param entityClass 实体类
* @return 元数据 * @return 元数据
*/ */
private EntityMeta getEntityMeta(Class<?> entityClass) { protected EntityMeta getEntityMeta(Class<?> entityClass) {
return ENTITY_META_CACHE.computeIfAbsent(entityClass, EntityMeta::new); return ENTITY_META_CACHE.computeIfAbsent(entityClass, EntityMeta::new);
} }
@ -295,7 +295,7 @@ public class SQLProvider {
* @author 夜雨 * @author 夜雨
* @since 2025-02-05 23:47 * @since 2025-02-05 23:47
*/ */
private static class EntityMeta { protected static class EntityMeta {
/** 实体类 */ /** 实体类 */
final Class<?> entityClass; final Class<?> entityClass;
@ -375,6 +375,42 @@ public class SQLProvider {
canDelete = Deletable.class.isAssignableFrom(entityClass); canDelete = Deletable.class.isAssignableFrom(entityClass);
canDestroy = Destroyable.class.isAssignableFrom(entityClass); canDestroy = Destroyable.class.isAssignableFrom(entityClass);
} }
public Class<?> getEntityClass() {
return entityClass;
}
public String getTable() {
return table;
}
public String getSelectAllClause() {
return selectAllClause;
}
public FieldColumn getIdFieldColumn() {
return idFieldColumn;
}
public List<FieldColumn> getFieldColumnList() {
return fieldColumnList;
}
public boolean canCreate() {
return canCreate;
}
public boolean canUpdate() {
return canUpdate;
}
public boolean canDelete() {
return canDelete;
}
public boolean canDestroy() {
return canDestroy;
}
} }
/** /**
@ -383,7 +419,7 @@ public class SQLProvider {
* @author 夜雨 * @author 夜雨
* @since 2025-02-07 09:54 * @since 2025-02-07 09:54
*/ */
private static class FieldColumn { protected static class FieldColumn {
/** 字段 */ /** 字段 */
final Field field; final Field field;
@ -421,5 +457,29 @@ public class SQLProvider {
isAutoUpperUUID = false; isAutoUpperUUID = false;
} }
} }
public Field getField() {
return field;
}
public String getFieldName() {
return fieldName;
}
public String getColumnName() {
return columnName;
}
public boolean isId() {
return isId;
}
public boolean isAutoUUID() {
return isAutoUUID;
}
public boolean isAutoUpperUUID() {
return isAutoUpperUUID;
}
} }
} }

View File

@ -1,4 +1,5 @@
invalid.arg=无效的参数 invalid.arg=无效的参数
invalid.body=无效的请求体 invalid.body=无效的请求体
invalid.header=无效的请求头 invalid.header=无效的请求头
invalid.request=无效的请求
service.error=服务错误 service.error=服务错误