Commit fd9c4a7a authored by liming's avatar liming

feat(初始化项目): 增加缓存和登录用户注入

parent 6fe0379a
...@@ -26,6 +26,20 @@ ...@@ -26,6 +26,20 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>io.springfox</groupId> <groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId> <artifactId>springfox-boot-starter</artifactId>
...@@ -50,5 +64,20 @@ ...@@ -50,5 +64,20 @@
<groupId>javax.activation</groupId> <groupId>javax.activation</groupId>
<artifactId>activation</artifactId> <artifactId>activation</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.antai.sport.http.server</groupId>
<artifactId>profile</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.antai.sport.http.server</groupId>
<artifactId>repository</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.antai.sport.http.server</groupId>
<artifactId>constants</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies> </dependencies>
</project> </project>
\ No newline at end of file
package com.antai.sport.http.server.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Author liming
* @Date 2021/8/23 1:59
* @Email lmmax@126.com
* @Description:
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginSportUser {
}
package com.antai.sport.http.server.common.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
/**
* @author liming on 2018/12/14.
*/
@Configuration
@EnableCaching
@ConfigurationProperties(prefix = "spring.cache.redis")
public class RedisCacheConfig extends CachingConfigurerSupport {
private Duration timeToLive = Duration.ZERO;
public void setTimeToLive(Duration timeToLive) {
this.timeToLive = timeToLive;
}
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(this.timeToLive)
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
.disableCachingNullValues();
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.transactionAware()
.build();
}
@Bean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(keySerializer());
redisTemplate.setHashKeySerializer(keySerializer());
redisTemplate.setValueSerializer(valueSerializer());
redisTemplate.setHashValueSerializer(valueSerializer());
return redisTemplate;
}
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
private RedisSerializer<Object> valueSerializer() {
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
om.registerModule(new JavaTimeModule());
serializer.setObjectMapper(om);
return serializer;
}
}
package com.antai.sport.http.server.common.jackson;
import com.fasterxml.jackson.core.io.NumberInput;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import java.text.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Default {@link DateFormat} implementation used by standard Date
* serializers and deserializers. For serialization defaults to using
* an ISO-8601 compliant format (format String "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
* and for deserialization, both ISO-8601 and RFC-1123.
*/
@SuppressWarnings("serial")
public class StdDateFormat
extends DateFormat {
/* 24-Jun-2017, tatu: Finally rewrote deserialization to use basic Regex
* instead of SimpleDateFormat; partly for better concurrency, partly
* for easier enforcing of specific rules. Heavy lifting done by Calendar,
* anyway.
*/
protected final static String PATTERN_PLAIN_STR = "\\d\\d\\d\\d[-]\\d\\d[-]\\d\\d";
protected final static Pattern PATTERN_PLAIN = Pattern.compile(PATTERN_PLAIN_STR);
protected final static Pattern PATTERN_ISO8601;
static {
Pattern p = null;
try {
p = Pattern.compile(PATTERN_PLAIN_STR
+ "[T]\\d\\d[:]\\d\\d(?:[:]\\d\\d)?" // hours, minutes, optional seconds
+ "(\\.\\d+)?" // optional second fractions
+ "(Z|[+-]\\d\\d(?:[:]?\\d\\d)?)?" // optional timeoffset/Z
);
} catch (Throwable t) {
throw new RuntimeException(t);
}
PATTERN_ISO8601 = p;
}
/**
* Defines a commonly used date format that conforms
* to ISO-8601 date formatting standard, when it includes basic undecorated
* timezone definition.
*/
public final static String DATE_FORMAT_STR_ISO8601 = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
/**
* ISO-8601 with just the Date part, no time: needed for error messages
*/
protected final static String DATE_FORMAT_STR_PLAIN = "yyyy-MM-dd";
/**
* This constant defines the date format specified by
* RFC 1123 / RFC 822. USED for parsing via `SimpleDateFormat` as well as
* error messages.
*/
protected final static String DATE_FORMAT_STR_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz";
/**
* yyyy-MM-dd HH:mm:ss.SSS
*/
// protected final static String DATE_FORMAT_TO_ALL= "yyyy-MM-dd HH:mm:ss.SSS";
/**
* yyyy-MM-dd HH:mm:ss
*/
protected final static String DATE_FORMAT_TO_SECOND = "yyyy-MM-dd HH:mm:ss";
/**
* For error messages we'll also need a list of all formats.
*/
protected final static String[] ALL_FORMATS = new String[]{
DATE_FORMAT_STR_ISO8601,
"yyyy-MM-dd'T'HH:mm:ss.SSS", // ISO-8601 but no timezone
DATE_FORMAT_STR_RFC1123,
DATE_FORMAT_STR_PLAIN,
// DATE_FORMAT_TO_ALL,
DATE_FORMAT_TO_SECOND
};
/**
* By default we use UTC for everything, with Jackson 2.7 and later
* (2.6 and earlier relied on GMT)
*/
protected final static TimeZone DEFAULT_TIMEZONE;
static {
DEFAULT_TIMEZONE = TimeZone.getTimeZone("UTC"); // since 2.7
}
protected final static Locale DEFAULT_LOCALE = Locale.US;
protected final static DateFormat DATE_FORMAT_RFC1123;
protected final static DateFormat DATE_FORMAT_ISO8601;
/* Let's construct "blueprint" date format instances: cannot be used
* as is, due to thread-safety issues, but can be used for constructing
* actual instances more cheaply (avoids re-parsing).
*/
static {
// Another important thing: let's force use of default timezone for
// baseline DataFormat objects
DATE_FORMAT_RFC1123 = new SimpleDateFormat(DATE_FORMAT_STR_RFC1123, DEFAULT_LOCALE);
DATE_FORMAT_RFC1123.setTimeZone(DEFAULT_TIMEZONE);
DATE_FORMAT_ISO8601 = new SimpleDateFormat(DATE_FORMAT_STR_ISO8601, DEFAULT_LOCALE);
DATE_FORMAT_ISO8601.setTimeZone(DEFAULT_TIMEZONE);
}
/**
* A singleton instance can be used for cloning purposes, as a blueprint of sorts.
*/
public final static StdDateFormat instance = new StdDateFormat();
/**
* Blueprint "Calendar" instance for use during formatting. Cannot be used as is,
* due to thread-safety issues, but can be used for constructing actual instances
* more cheaply by cloning.
*
* @since 2.9.1
*/
protected static final Calendar CALENDAR = new GregorianCalendar(DEFAULT_TIMEZONE, DEFAULT_LOCALE);
/**
* Caller may want to explicitly override timezone to use; if so,
* we will have non-null value here.
*/
protected transient TimeZone timezone;
protected final Locale _locale;
/**
* Explicit override for leniency, if specified.
* <p>
* Cannot be `final` because {@link #setLenient(boolean)} returns
* `void`.
*/
protected Boolean _lenient;
/**
* Lazily instantiated calendar used by this instance for serialization ({@link #format(Date)}).
*
* @since 2.9.1
*/
private transient Calendar _calendar;
private transient DateFormat _formatRFC1123;
/**
* Whether the TZ offset must be formatted with a colon between hours and minutes ({@code HH:mm} format)
* <p>
* NOTE: default changed to `true` in Jackson 3.0; was `false` earlier.
*/
protected boolean _tzSerializedWithColon = true;
/*
/**********************************************************
/* Life cycle, accessing singleton "standard" formats
/**********************************************************
*/
public StdDateFormat() {
_locale = DEFAULT_LOCALE;
}
protected StdDateFormat(TimeZone tz, Locale loc, Boolean lenient) {
this(tz, loc, lenient, false);
}
/**
* @since 2.9.1
*/
protected StdDateFormat(TimeZone tz, Locale loc, Boolean lenient,
boolean formatTzOffsetWithColon) {
timezone = tz;
_locale = loc;
_lenient = lenient;
_tzSerializedWithColon = formatTzOffsetWithColon;
}
public static TimeZone getDefaultTimeZone() {
return DEFAULT_TIMEZONE;
}
/**
* Method used for creating a new instance with specified timezone;
* if no timezone specified, defaults to the default timezone (UTC).
*/
public StdDateFormat withTimeZone(TimeZone tz) {
if (tz == null) {
tz = DEFAULT_TIMEZONE;
}
if ((tz == timezone) || tz.equals(timezone)) {
return this;
}
return new StdDateFormat(tz, _locale, _lenient, _tzSerializedWithColon);
}
/**
* "Mutant factory" method that will return an instance that uses specified
* {@code Locale}:
* either {@code this} instance (if setting would not change), or newly
* constructed instance with different {@code Locale} to use.
*/
public StdDateFormat withLocale(Locale loc) {
if (loc.equals(_locale)) {
return this;
}
return new StdDateFormat(timezone, loc, _lenient, _tzSerializedWithColon);
}
/**
* "Mutant factory" method that will return an instance that has specified leniency
* setting: either {@code this} instance (if setting would not change), or newly
* constructed instance.
*
* @since 2.9
*/
public StdDateFormat withLenient(Boolean b) {
if (_equals(b, _lenient)) {
return this;
}
return new StdDateFormat(timezone, _locale, b, _tzSerializedWithColon);
}
/**
* "Mutant factory" method that will return an instance that has specified
* handling of colon when serializing timezone (timezone either written
* like {@code +0500} or {@code +05:00}):
* either {@code this} instance (if setting would not change), or newly
* constructed instance with desired setting for colon inclusion.
* <p>
* NOTE: does NOT affect deserialization as colon is optional accepted
* but not required -- put another way, either serialization is accepted
* by this class.
*
* @since 2.9.1
*/
public StdDateFormat withColonInTimeZone(boolean b) {
if (_tzSerializedWithColon == b) {
return this;
}
return new StdDateFormat(timezone, _locale, _lenient, b);
}
@Override
public StdDateFormat clone() {
// Although there is that much state to share, we do need to
// orchestrate a bit, mostly since timezones may be changed
return new StdDateFormat(timezone, _locale, _lenient, _tzSerializedWithColon);
}
/*
/**********************************************************
/* Public API, configuration
/**********************************************************
*/
@Override // since 2.6
public TimeZone getTimeZone() {
return timezone;
}
@Override
public void setTimeZone(TimeZone tz) {
/* DateFormats are timezone-specific (via Calendar contained),
* so need to reset instances if timezone changes:
*/
if (!tz.equals(timezone)) {
_clearFormats();
timezone = tz;
}
}
/**
* Need to override since we need to keep track of leniency locally,
* and not via underlying {@link Calendar} instance like system class
* does.
*/
@Override // since 2.7
public void setLenient(boolean enabled) {
Boolean newValue = Boolean.valueOf(enabled);
if (!_equals(newValue, _lenient)) {
_lenient = newValue;
// and since leniency settings may have been used:
_clearFormats();
}
}
@Override // since 2.7
public boolean isLenient() {
// default is, I believe, true
return (_lenient == null) || _lenient.booleanValue();
}
/**
* Accessor for checking whether this instance would include colon
* within timezone serialization or not: if {code true}, timezone offset
* is serialized like {@code -06:00}; if {code false} as {@code -0600}.
* <p>
* NOTE: only relevant for serialization (formatting), as deserialization
* (parsing) always accepts optional colon but does not require it, regardless
* of this setting.
*
* @return {@code true} if a colon is to be inserted between the hours and minutes
* of the TZ offset when serializing as String; otherwise {@code false}
* @since 2.9.1
*/
public boolean isColonIncludedInTimeZone() {
return _tzSerializedWithColon;
}
/*
/**********************************************************
/* Public API, parsing
/**********************************************************
*/
@Override
public Date parse(String dateStr) throws ParseException {
dateStr = dateStr.trim();
ParsePosition pos = new ParsePosition(0);
Date dt = null;
try {
dt = DateUtils.parseDate(dateStr, DATE_FORMAT_TO_SECOND, DATE_FORMAT_STR_PLAIN);
} catch (Exception ex) {
}
if (dt != null) {
return dt;
} else {
dt = _parseDate(dateStr, pos);
if (dt != null) {
return dt;
}
}
StringBuilder sb = new StringBuilder();
for (String f : ALL_FORMATS) {
if (sb.length() > 0) {
sb.append("\", \"");
} else {
sb.append('"');
}
sb.append(f);
}
sb.append('"');
throw new ParseException
(String.format("Cannot parse date \"%s\": not compatible with any of standard forms (%s)",
dateStr, sb.toString()), pos.getErrorIndex());
}
// 24-Jun-2017, tatu: I don't think this ever gets called. So could... just not implement?
@Override
public Date parse(String dateStr, ParsePosition pos) {
try {
return _parseDate(dateStr, pos);
} catch (ParseException e) {
// may look weird but this is what `DateFormat` suggest to do...
}
return null;
}
protected Date _parseDate(String dateStr, ParsePosition pos) throws ParseException {
if (looksLikeISO8601(dateStr)) { // also includes "plain"
return parseAsISO8601(dateStr, pos);
}
// Also consider "stringified" simple time stamp
int i = dateStr.length();
while (--i >= 0) {
char ch = dateStr.charAt(i);
if (ch < '0' || ch > '9') {
// 07-Aug-2013, tatu: And [databind#267] points out that negative numbers should also work
if (i > 0 || ch != '-') {
break;
}
}
}
if ((i < 0)
// let's just assume negative numbers are fine (can't be RFC-1123 anyway); check length for positive
&& (dateStr.charAt(0) == '-' || NumberInput.inLongRange(dateStr, false))) {
return _parseDateFromLong(dateStr, pos);
}
// Otherwise, fall back to using RFC 1123. NOTE: call will NOT throw, just returns `null`
return parseAsRFC1123(dateStr, pos);
}
/*
/**********************************************************
/* Public API, writing
/**********************************************************
*/
@Override
public StringBuffer format(Date date, StringBuffer toAppendTo,
FieldPosition fieldPosition) {
TimeZone tz = timezone;
if (tz == null) {
tz = DEFAULT_TIMEZONE;
}
toAppendTo.append(DateFormatUtils.format(date,DATE_FORMAT_TO_SECOND,tz));
// _format(tz, _locale, date, toAppendTo);
return toAppendTo;
}
protected void _format(TimeZone tz, Locale loc, Date date,
StringBuffer buffer) {
Calendar cal = _getCalendar(tz);
cal.setTime(date);
pad4(buffer, cal.get(Calendar.YEAR));
buffer.append('-');
pad2(buffer, cal.get(Calendar.MONTH) + 1);
buffer.append('-');
pad2(buffer, cal.get(Calendar.DAY_OF_MONTH));
buffer.append('T');
pad2(buffer, cal.get(Calendar.HOUR_OF_DAY));
buffer.append(':');
pad2(buffer, cal.get(Calendar.MINUTE));
buffer.append(':');
pad2(buffer, cal.get(Calendar.SECOND));
buffer.append('.');
pad3(buffer, cal.get(Calendar.MILLISECOND));
int offset = tz.getOffset(cal.getTimeInMillis());
if (offset != 0) {
int hours = Math.abs((offset / (60 * 1000)) / 60);
int minutes = Math.abs((offset / (60 * 1000)) % 60);
buffer.append(offset < 0 ? '-' : '+');
pad2(buffer, hours);
if (_tzSerializedWithColon) {
buffer.append(':');
}
pad2(buffer, minutes);
} else {
// 24-Jun-2017, tatu: While `Z` would be conveniently short, older specs
// mandate use of full `+0000`
// formatted.append('Z');
if (_tzSerializedWithColon) {
buffer.append("+00:00");
} else {
buffer.append("+0000");
}
}
}
private static void pad2(StringBuffer buffer, int value) {
int tens = value / 10;
if (tens == 0) {
buffer.append('0');
} else {
buffer.append((char) ('0' + tens));
value -= 10 * tens;
}
buffer.append((char) ('0' + value));
}
private static void pad3(StringBuffer buffer, int value) {
int h = value / 100;
if (h == 0) {
buffer.append('0');
} else {
buffer.append((char) ('0' + h));
value -= (h * 100);
}
pad2(buffer, value);
}
private static void pad4(StringBuffer buffer, int value) {
int h = value / 100;
if (h == 0) {
buffer.append('0').append('0');
} else {
pad2(buffer, h);
value -= (100 * h);
}
pad2(buffer, value);
}
/*
/**********************************************************
/* Std overrides
/**********************************************************
*/
@Override
public String toString() {
return String.format("DateFormat %s: (timezone: %s, locale: %s, lenient: %s)",
getClass().getName(), timezone, _locale, _lenient);
}
public String toPattern() { // same as SimpleDateFormat
StringBuilder sb = new StringBuilder(100);
sb.append("[one of: '")
.append(DATE_FORMAT_STR_ISO8601)
.append("', '")
.append(DATE_FORMAT_STR_RFC1123)
.append("' (")
;
sb.append(Boolean.FALSE.equals(_lenient) ?
"strict" : "lenient")
.append(")]");
return sb.toString();
}
@Override
public boolean equals(Object o) {
return (o == this);
}
@Override
public int hashCode() {
return System.identityHashCode(this);
}
/*
/**********************************************************
/* Helper methods, parsing
/**********************************************************
*/
/**
* Helper method used to figure out if input looks like valid
* ISO-8601 string.
*/
protected boolean looksLikeISO8601(String dateStr) {
if (dateStr.length() >= 7 // really need 10, but...
&& Character.isDigit(dateStr.charAt(0))
&& Character.isDigit(dateStr.charAt(3))
&& dateStr.charAt(4) == '-'
&& Character.isDigit(dateStr.charAt(5))
) {
return true;
}
return false;
}
private Date _parseDateFromLong(String longStr, ParsePosition pos) throws ParseException {
long ts;
try {
ts = NumberInput.parseLong(longStr);
} catch (NumberFormatException e) {
throw new ParseException(String.format(
"Timestamp value %s out of 64-bit value range", longStr),
pos.getErrorIndex());
}
return new Date(ts);
}
protected Date parseAsISO8601(String dateStr, ParsePosition pos)
throws ParseException {
try {
return _parseAsISO8601(dateStr, pos);
} catch (IllegalArgumentException e) {
throw new ParseException(String.format("Cannot parse date \"%s\", problem: %s",
dateStr, e.getMessage()),
pos.getErrorIndex());
}
}
protected Date _parseAsISO8601(String dateStr, ParsePosition bogus)
throws IllegalArgumentException, ParseException {
final int totalLen = dateStr.length();
// actually, one short-cut: if we end with "Z", must be UTC
TimeZone tz = DEFAULT_TIMEZONE;
if ((timezone != null) && ('Z' != dateStr.charAt(totalLen - 1))) {
tz = timezone;
}
Calendar cal = _getCalendar(tz);
cal.clear();
String formatStr;
if (totalLen <= 10) {
Matcher m = PATTERN_PLAIN.matcher(dateStr);
if (m.matches()) {
int year = _parse4D(dateStr, 0);
int month = _parse2D(dateStr, 5) - 1;
int day = _parse2D(dateStr, 8);
cal.set(year, month, day, 0, 0, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime();
}
formatStr = DATE_FORMAT_STR_PLAIN;
} else {
Matcher m = PATTERN_ISO8601.matcher(dateStr);
if (m.matches()) {
// Important! START with optional time zone; otherwise Calendar will explode
int start = m.start(2);
int end = m.end(2);
int len = end - start;
if (len > 1) { // 0 -> none, 1 -> 'Z'
// NOTE: first char is sign; then 2 digits, then optional colon, optional 2 digits
int offsetSecs = _parse2D(dateStr, start + 1) * 3600; // hours
if (len >= 5) {
offsetSecs += _parse2D(dateStr, end - 2) * 60; // minutes
}
if (dateStr.charAt(start) == '-') {
offsetSecs *= -1000;
} else {
offsetSecs *= 1000;
}
cal.set(Calendar.ZONE_OFFSET, offsetSecs);
// 23-Jun-2017, tatu: Not sure why, but this appears to be needed too:
cal.set(Calendar.DST_OFFSET, 0);
}
int year = _parse4D(dateStr, 0);
int month = _parse2D(dateStr, 5) - 1;
int day = _parse2D(dateStr, 8);
// So: 10 chars for date, then `T`, so starts at 11
int hour = _parse2D(dateStr, 11);
int minute = _parse2D(dateStr, 14);
// Seconds are actually optional... so
int seconds;
if ((totalLen > 16) && dateStr.charAt(16) == ':') {
seconds = _parse2D(dateStr, 17);
} else {
seconds = 0;
}
cal.set(year, month, day, hour, minute, seconds);
// Optional milliseconds
start = m.start(1) + 1;
end = m.end(1);
int msecs = 0;
if (start >= end) { // no fractional
cal.set(Calendar.MILLISECOND, 0);
} else {
// first char is '.', but rest....
msecs = 0;
final int fractLen = end - start;
switch (fractLen) {
default: // [databind#1745] Allow longer fractions... for now, cap at nanoseconds tho
if (fractLen > 9) { // only allow up to nanos
throw new ParseException(String.format(
"Cannot parse date \"%s\": invalid fractional seconds '%s'; can use at most 9 digits",
dateStr, m.group(1).substring(1)
), start);
}
// fall through
case 3:
//在case1终止
msecs += (dateStr.charAt(start + 2) - '0');
case 2:
//在case1终止
msecs += 10 * (dateStr.charAt(start + 1) - '0');
case 1:
msecs += 100 * (dateStr.charAt(start) - '0');
break;
case 0:
break;
}
cal.set(Calendar.MILLISECOND, msecs);
}
return cal.getTime();
}
formatStr = DATE_FORMAT_STR_ISO8601;
}
throw new ParseException
(String.format("Cannot parse date \"%s\": while it seems to fit format '%s', parsing fails (leniency? %s)",
dateStr, formatStr, _lenient),
// [databind#1742]: Might be able to give actual location, some day, but for now
// we can't give anything more indicative
0);
}
private static int _parse4D(String str, int index) {
return (1000 * (str.charAt(index) - '0'))
+ (100 * (str.charAt(index + 1) - '0'))
+ (10 * (str.charAt(index + 2) - '0'))
+ (str.charAt(index + 3) - '0');
}
private static int _parse2D(String str, int index) {
return (10 * (str.charAt(index) - '0'))
+ (str.charAt(index + 1) - '0');
}
protected Date parseAsRFC1123(String dateStr, ParsePosition pos) {
if (_formatRFC1123 == null) {
_formatRFC1123 = _cloneFormat(DATE_FORMAT_RFC1123, DATE_FORMAT_STR_RFC1123,
timezone, _locale, _lenient);
}
return _formatRFC1123.parse(dateStr, pos);
}
/*
/**********************************************************
/* Helper methods, other
/**********************************************************
*/
private final static DateFormat _cloneFormat(DateFormat df, String format,
TimeZone tz, Locale loc, Boolean lenient) {
if (!loc.equals(DEFAULT_LOCALE)) {
df = new SimpleDateFormat(format, loc);
df.setTimeZone((tz == null) ? DEFAULT_TIMEZONE : tz);
} else {
df = (DateFormat) df.clone();
if (tz != null) {
df.setTimeZone(tz);
}
}
if (lenient != null) {
df.setLenient(lenient.booleanValue());
}
return df;
}
protected void _clearFormats() {
_formatRFC1123 = null;
}
protected Calendar _getCalendar(TimeZone tz) {
Calendar cal = _calendar;
if (cal == null) {
_calendar = cal = (Calendar) CALENDAR.clone();
}
if (!cal.getTimeZone().equals(tz)) {
cal.setTimeZone(tz);
}
cal.setLenient(isLenient());
return cal;
}
protected static <T> boolean _equals(T value1, T value2) {
if (value1 == value2) {
return true;
}
return (value1 != null) && value1.equals(value2);
}
}
\ No newline at end of file
package com.antai.sport.http.server.common.resolver;
import com.antai.sport.http.server.common.exception.BusinessException;
import com.antai.sport.http.server.common.jwt.TokenService;
import com.antai.sport.http.server.repository.sport.entity.SportUser;
import com.antai.sport.http.server.repository.sport.mapper.SportUserCacheMapper;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.annotation.Resource;
public class SportUserArgumentResolver implements HandlerMethodArgumentResolver {
@Value("${project.header-token-key}")
private String headerTokenKey;
@Value("${project.jwt.subject-key}")
private String subjectKey;
@Resource
private SportUserCacheMapper sportUserCacheMapper;
@Resource
private TokenService tokenService;
/**
* 判断是否支持要转换的参数类型
*/
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.getParameterType().isAssignableFrom(SportUser.class);
}
/**
* 当支持后进行相应的转换 做业务操作
*/
@Override
public Object resolveArgument(MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer,
NativeWebRequest request,
WebDataBinderFactory webDataBinderFactory) throws Exception {
//从请求头中获参数信息
String token = request.getHeader(headerTokenKey);
if (token == null || token.isEmpty()) {
throw new BusinessException("token信息错误");
}
Claims claims = tokenService.validateToken(token);
if (claims == null) {
throw new BusinessException("token解析异常");
}
String phone = claims.get(subjectKey).toString();
SportUser sportUser = sportUserCacheMapper.getByPhone(phone);
if (sportUser == null) {
throw new BusinessException("当前用户不存在");
}
return sportUser;
}
}
...@@ -18,37 +18,11 @@ ...@@ -18,37 +18,11 @@
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.antai.sport.http.server</groupId>
<artifactId>profile</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.antai.sport.http.server</groupId>
<artifactId>repository</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency> <dependency>
<groupId>com.antai.sport.http.server</groupId> <groupId>com.antai.sport.http.server</groupId>
<artifactId>common</artifactId> <artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency>
<groupId>com.antai.sport.http.server</groupId>
<artifactId>constants</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
......
...@@ -3,6 +3,7 @@ package com.antai.sport.http.server.game.api; ...@@ -3,6 +3,7 @@ package com.antai.sport.http.server.game.api;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import springfox.documentation.oas.annotations.EnableOpenApi; import springfox.documentation.oas.annotations.EnableOpenApi;
...@@ -16,7 +17,7 @@ import springfox.documentation.oas.annotations.EnableOpenApi; ...@@ -16,7 +17,7 @@ import springfox.documentation.oas.annotations.EnableOpenApi;
@SpringBootApplication @SpringBootApplication
@EnableOpenApi @EnableOpenApi
@ComponentScan(value = "com.antai") @ComponentScan(value = "com.antai")
@MapperScan("com.antai.**.repository") @MapperScan("com.antai")
public class GameApiApplication { public class GameApiApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(GameApiApplication.class, args); SpringApplication.run(GameApiApplication.class, args);
......
package com.antai.sport.http.server.game.api.business.sport.user; package com.antai.sport.http.server.game.api.business.sport.user;
import com.antai.sport.http.server.common.annotation.LoginSportUser;
import com.antai.sport.http.server.common.base.Result; import com.antai.sport.http.server.common.base.Result;
import com.antai.sport.http.server.game.api.business.sport.user.dto.ReqLogin; import com.antai.sport.http.server.game.api.business.sport.user.dto.ReqLogin;
import com.antai.sport.http.server.repository.sport.entity.SportUser;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
...@@ -34,7 +36,8 @@ public class SportUserController { ...@@ -34,7 +36,8 @@ public class SportUserController {
} }
@GetMapping("/test") @GetMapping("/test")
public ResponseEntity<Result> test() { public ResponseEntity<Result> test(@LoginSportUser SportUser sportUser) {
System.out.println(1);
return success("test"); return success("test");
} }
} }
package com.antai.sport.http.server.game.api.config; package com.antai.sport.http.server.game.api.config;
import com.antai.sport.http.server.common.resolver.SportUserArgumentResolver;
import com.antai.sport.http.server.game.api.interceptor.TokenInterceptor; import com.antai.sport.http.server.game.api.interceptor.TokenInterceptor;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
...@@ -44,6 +46,16 @@ public class WebMvcConfig implements WebMvcConfigurer { ...@@ -44,6 +46,16 @@ public class WebMvcConfig implements WebMvcConfigurer {
return new TokenInterceptor(); return new TokenInterceptor();
} }
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(sportUserArgumentResolver());
}
@Bean
SportUserArgumentResolver sportUserArgumentResolver() {
return new SportUserArgumentResolver();
}
public void setPermitUrl(List<String> permitUrl) { public void setPermitUrl(List<String> permitUrl) {
this.permitUrl = permitUrl; this.permitUrl = permitUrl;
} }
......
server:
port: 8080
spring: spring:
profiles: profiles:
include: include:
- common-db-dev - common-db-dev
redis:
database: 0
host: 127.0.0.1
port: 6379
password: 123
swagger: swagger:
enable: true enable: true
spring: spring:
profiles: profiles:
include: include:
- common-db-prod - common-db-prod
\ No newline at end of file redis:
database: 0
host: 127.0.0.1
port: 6379
password: 123
\ No newline at end of file
...@@ -5,6 +5,22 @@ spring: ...@@ -5,6 +5,22 @@ spring:
use-legacy-processing: true use-legacy-processing: true
profiles: profiles:
active: dev active: dev
cache:
redis:
use-key-prefix: true
time-to-live: 1d
redis:
timeout: 10s
jedis:
pool:
max-idle: 500
min-idle: 50
max-wait: -1s
max-active: -1
jackson:
date-format: com.antai.sport.http.server.common.jackson.StdDateFormat
time-zone: GMT+8
default-property-inclusion: always
project: project:
header-token-key: Authorization header-token-key: Authorization
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
<jjwt.version>0.7.0</jjwt.version> <jjwt.version>0.7.0</jjwt.version>
<jaxb.version>2.3.0</jaxb.version> <jaxb.version>2.3.0</jaxb.version>
<activation.version>1.1.1</activation.version> <activation.version>1.1.1</activation.version>
<commons.lang3.version>3.7</commons.lang3.version>
<maven.compiler.source>11</maven.compiler.source> <maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target> <maven.compiler.target>11</maven.compiler.target>
...@@ -51,6 +52,11 @@ ...@@ -51,6 +52,11 @@
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons.lang3.version}</version>
</dependency>
</dependencies> </dependencies>
<dependencyManagement> <dependencyManagement>
......
package com.antai.sport.http.server.repository.sport.mapper;
import com.antai.sport.http.server.repository.sport.entity.SportUser;
import org.springframework.cache.annotation.Cacheable;
/**
* @Author liming
* @Date 2021/8/23 0:59
* @Email lmmax@126.com
* @Description:
*/
public interface SportUserCacheMapper {
@Cacheable(value = "sport_user", key = "#p0", unless = "#result == null")
SportUser getByPhone(String username);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.antai.sport.http.server.repository.sport.mapper.SportUserCacheMapper">
<select id="getByPhone" parameterType="java.lang.String"
resultType="com.antai.sport.http.server.repository.sport.entity.SportUser">
select *
from sport_user t1
where t1.phone = #{username}
</select>
</mapper>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment