提交新功能 分销商 积分 会员体系

This commit is contained in:
xwxuan
2024-02-08 20:44:58 +08:00
parent 0e255d6c3a
commit 6f5e6e4662
928 changed files with 39318 additions and 1408 deletions

View File

@ -137,6 +137,10 @@
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
</dependencies>

View File

@ -194,4 +194,27 @@ public interface ShopConstants {
//快递查询接口Logistic
String KDNIAO_LOGISTIC_QUERY="https://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx";
/**
* redis营销活动状态变更key
*/
String CAMPAIGN_CHANGE = "campaign-change-queue";
/**
* redis广告弹窗状态变更key
*/
String POPUP_CHANGE = "popup-change-queue";
/**
* redis拼团状态变更key
*/
String TEAMWORK_CHANGE = "teamwork-change-queue";
String DAY_FORMAT_STR = "yyyy-MM-dd";
/**
* redis 订单收货后不可关闭售后 key
*/
String CLOSE_AFTER_SALE_KEY = "close-after-sale-key";
}

View File

@ -0,0 +1,22 @@
package co.yixiang.yshop.framework.common.enums;
import lombok.Getter;
/**
* @author pepis
* @apiNote 开启关闭枚举
**/
@Getter
public enum EnableEnum {
DISABLE(0,"禁用"),
ENABLE(1,"启用"),
;
private final Integer value;
private final String desc;
EnableEnum(Integer value, String desc) {
this.value = value;
this.desc = desc;
}
}

View File

@ -20,11 +20,13 @@ public enum OrderInfoEnum {
STATUS_APPLY_REFUND(-1,"申请退款"),
STATUS_REFUND_SUCCESS(-2,"退款成功"),
STATUS_GROUP_FAILURE(-4,"成团失败"),
STATUS_DEFAULT(0,"默认"),
STATUS_WAIT_RECEIVED(1,"待收货"),
STATUS_RECEIVED(2,"已收货"),
STATUS_FINISHED(3,"已完成"),
STATUS_CANCEL(4,"取消"),
STATUS_WAIT_GROUP(5,"待成团"),
PAY_STATUS_UNPAID(0,"未支付"),
PAY_STATUS_HAVE_PAID(1,"已支付"),
@ -61,7 +63,9 @@ public enum OrderInfoEnum {
SHIPPING_TYPE_STORE_PICKUP(2,"门店自提"),
UNABLE_AFTER_SALES(0,"不能售后"),
ABLE_AFTER_SALES(1,"能售后");
ABLE_AFTER_SALES(1,"能售后"),
CAMPAIGN_ORDER(2,"活动订单");

View File

@ -1,5 +1,6 @@
package co.yixiang.yshop.framework.common.util.date;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import java.time.*;
@ -170,4 +171,82 @@ public class DateUtils {
return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now());
}
/**
* 判断当前时间是否处在开始时间和结束时间之间
*
* @param start
* @param end
* @return 是否
*/
public static boolean isBelong(LocalDateTime start,LocalDateTime end) {
return isBefore(end) && isAfter(start);
}
/**
* 判断当前时间是否处在传入时间前
*
* @param time
* @return 是否
*/
public static boolean isBefore(LocalDateTime time) {
return toTimeStamp(time) > System.currentTimeMillis();
}
/**
* 判断当前时间是否处在传入时间后
*
* @param time 时间
* @return 是否
*/
public static boolean isAfter(LocalDateTime time) {
return toTimeStamp(time) < System.currentTimeMillis();
}
/**
* 传入时间累加
*
* @param time 时间
* @param
* @return
*/
public static LocalDateTime getMoreMinuteAfter(LocalDateTime time, int minute) {
return time.plusMinutes(minute);
}
/**
* 计算与当前时间的差值(毫秒)
*
* @param time
* @param
* @return
*/
public static long getMoreMillisecondAfter(LocalDateTime time) {
return toTimeStamp(time) - System.currentTimeMillis();
}
/**
* 计算时间戳
*
* @param time
* @param
* @return
*/
public static long toTimeStamp(LocalDateTime time) {
return time.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
}
/**
* 判断两个日期是否同一天
* @param date1 /
* @param date2 /
* @return /
*/
public static Boolean isSameDay(Date date1,Date date2){
return DateUtil.formatDate(date1).equals(DateUtil.formatDate(date2));
}
}

View File

@ -3,10 +3,13 @@ package co.yixiang.yshop.framework.common.util.date;
import cn.hutool.core.date.LocalDateTimeUtil;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.TemporalAdjusters;
/**
* 时间工具类,用于 {@link LocalDateTime}
* 时间工具类,用于 {@link java.time.LocalDateTime}
*
* @author yshop
*/
@ -60,4 +63,42 @@ public class LocalDateTimeUtils {
return LocalDateTimeUtil.isIn(LocalDateTime.now(), startTime, endTime);
}
/**
* 本周开始时间
*
* @return
*/
public static LocalDateTime weekStartTime() {
LocalDate now = LocalDate.now();
return LocalDateTime.of(now.minusDays(now.getDayOfWeek().getValue() - 1), LocalTime.MIN);
}
/**
* 本周结束时间
*
* @return
*/
public static LocalDateTime weekEndTime() {
LocalDate now = LocalDate.now();
return LocalDateTime.of(now.plusDays(7 - now.getDayOfWeek().getValue()), LocalTime.MAX);
}
/**
* 本月开始时间
*
* @return
*/
public static LocalDateTime monthStartTime() {
return LocalDateTime.of(LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()), LocalTime.MIN);
}
/**
* 本月结束时间
*
* @return
*/
public static LocalDateTime monthEndTime() {
return LocalDateTime.of(LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()), LocalTime.MAX);
}
}

View File

@ -0,0 +1,46 @@
package co.yixiang.yshop.framework.common.util.distancecalculator;
/**
* @author pepis
* @apiNote
**/
public class DistanceCalculatorUtil {
public static void main(String[] args) {
double lat1 = 37.7749; // 第一个点的纬度
double lon1 = -122.4194; // 第一个点的经度
double lat2 = 34.0522; // 第二个点的纬度
double lon2 = -118.2437; // 第二个点的经度
double distance = calculateDistance(lat1, lon1, lat2, lon2);
System.out.println("Distance: " + distance + " m");
}
/**
* 计算两地之间距离
* @param lat1 第一个点的纬度
* @param lon1 第一个点的经度
* @param lat2 第二个点的纬度
* @param lon2 第二个点的经度
* @return 距离单位 米
*/
public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
// 地球半径,单位:千米
final double R = 6371.0;
// 将经纬度转换为弧度
double radLat1 = Math.toRadians(lat1);
double radLon1 = Math.toRadians(lon1);
double radLat2 = Math.toRadians(lat2);
double radLon2 = Math.toRadians(lon2);
// Haversine公式计算距离
double dlon = radLon2 - radLon1;
double dlat = radLat2 - radLat1;
double a = Math.pow(Math.sin(dlat / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(dlon / 2), 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
// 计算距离
return Math.round(R * c * 1000);
}
}

View File

@ -5,11 +5,13 @@ import cn.hutool.core.map.TableMap;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import okhttp3.*;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Map;
@ -39,12 +41,11 @@ public class HttpUtils {
/**
* 拼接 URL
*
* copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 append 方法
*
* @param base 基础 URL
* @param query 查询参数
* @param keys query 的 key对应的原本的 key 的映射。例如说 query 里有个 key 是 xx实际它的 key 是 extra_xx则通过 keys 里添加这个映射
* @param base 基础 URL
* @param query 查询参数
* @param keys query 的 key对应的原本的 key 的映射。例如说 query 里有个 key 是 xx实际它的 key 是 extra_xx则通过 keys 里添加这个映射
* @param fragment URL 的 fragment即拼接到 # 中
* @return 拼接后的 URL
*/
@ -109,7 +110,7 @@ public class HttpUtils {
authorization = Base64.decodeStr(authorization);
clientId = StrUtil.subBefore(authorization, ":", false);
clientSecret = StrUtil.subAfter(authorization, ":", false);
// 再从 Param 中获取
// 再从 Param 中获取
} else {
clientId = request.getParameter("client_id");
clientSecret = request.getParameter("client_secret");
@ -122,5 +123,31 @@ public class HttpUtils {
return null;
}
public static String getAppletNoticeToken(String appid, String secret) throws IOException {
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
Request request = new Request.Builder()
.url("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret)
.get()
.build();
Response response = client.newCall(request).execute();
assert response.body() != null;
return response.body().string();
}
public static String sendAppletNotice(String token,String bodyString) throws IOException {
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("text/plain");
RequestBody body = RequestBody.create(mediaType, bodyString);
Request request = new Request.Builder()
.url("https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info?access_token="+token)
.method("POST", body)
.build();
Response response = client.newCall(request).execute();
assert response.body() != null;
return response.body().string();
}
}

View File

@ -7,7 +7,7 @@ import cn.hutool.core.util.StrUtil;
import java.io.InputStream;
/**
* IO 工具类,用于 {@link IoUtil} 缺失的方法
* IO 工具类,用于 {@link cn.hutool.core.io.IoUtil} 缺失的方法
*
* @author yshop
*/

View File

@ -3,7 +3,7 @@ package co.yixiang.yshop.framework.common.util.object;
import co.yixiang.yshop.framework.common.pojo.PageParam;
/**
* {@link PageParam} 工具类
* {@link co.yixiang.yshop.framework.common.pojo.PageParam} 工具类
*
* @author yshop
*/

View File

@ -10,7 +10,7 @@ import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
/**
* {@link DataPermission} 注解的 Advisor 实现类
* {@link co.yixiang.yshop.framework.datapermission.core.annotation.DataPermission} 注解的 Advisor 实现类
*
* @author yshop
*/

View File

@ -43,7 +43,6 @@ public class SmsClientFactoryImpl implements SmsClientFactory {
/**
* 短信客户端 Map
* key渠道编码使用 {@link SmsChannelProperties#getCode()} ()}
*
* 注意,一些场景下,需要获得某个渠道类型的客户端,所以需要使用它。
* 例如说,解析短信接收结果,是相对通用的,不需要使用某个渠道编号的 {@link #channelIdClients}
*/

View File

@ -0,0 +1,52 @@
package co.yixiang.yshop.framework.weixin.config;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 微信小程序配置文件
*
* @author: moxiangrong
**/
@Configuration
@ConditionalOnClass(WxMaService.class)
@EnableConfigurationProperties(WxMiniProgramProperties.class)
public class WxMiniProgramConfig {
/*注入小程序相关配置*/
@Autowired
private WxMiniProgramProperties properties;
/**
* 配置默认参数
*/
@Bean
@ConditionalOnMissingBean
public WxMaConfig wxMaConfig() {
WxMaDefaultConfigImpl wxMaDefaultConfig = new WxMaDefaultConfigImpl();
//设置默认参数-appid,secret
wxMaDefaultConfig.setAppid(this.properties.getAppid());
wxMaDefaultConfig.setSecret(this.properties.getSecret());
return wxMaDefaultConfig;
}
/**
* 配置WxMaService
*/
@Bean
@ConditionalOnMissingBean
public WxMaService wxMaService(WxMaConfig wxMaConfig) {
WxMaService wxMaService = new WxMaServiceImpl();
wxMaService.setWxMaConfig(wxMaConfig);
return wxMaService;
}
}

View File

@ -0,0 +1,18 @@
package co.yixiang.yshop.framework.weixin.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
@ConfigurationProperties(prefix = "wx.miniapp")
@Validated
@Data
public class WxMiniProgramProperties {
/*微信小程序app_id*/
private String appid;
/*微信小程序app_secret*/
private String secret;
}

View File

@ -13,7 +13,6 @@ public class YshopFlowableConfiguration {
/**
* 参考 {@link org.flowable.spring.boot.FlowableJobConfiguration} 类,创建对应的 AsyncListenableTaskExecutor Bean
*
* 如果不创建会导致项目启动时Flowable 报错的问题
*/
@Bean

View File

@ -5,7 +5,7 @@ import co.yixiang.yshop.framework.quartz.core.handler.JobHandlerInvoker;
import org.quartz.*;
/**
* {@link Scheduler} 的管理器,负责创建任务
* {@link org.quartz.Scheduler} 的管理器,负责创建任务
*
* 考虑到实现的简洁性,我们使用 jobHandlerName 作为唯一标识,即:
* 1. Job 的 {@link JobDetail#getKey()}

View File

@ -23,7 +23,7 @@ public class DefaultStreamMessageListenerContainerX<K, V extends Record<K, ?>> e
/**
* 参考 {@link StreamMessageListenerContainer#create(RedisConnectionFactory, StreamMessageListenerContainerOptions)} 的实现
*/
public static <K, V extends Record<K, ?>> StreamMessageListenerContainer<K, V> create(RedisConnectionFactory connectionFactory, StreamMessageListenerContainerOptions<K, V> options) {
public static <K, V extends Record<K, ?>> StreamMessageListenerContainer<K, V> create(RedisConnectionFactory connectionFactory, StreamMessageListenerContainer.StreamMessageListenerContainerOptions<K, V> options) {
Assert.notNull(connectionFactory, "RedisConnectionFactory must not be null!");
Assert.notNull(options, "StreamMessageListenerContainerOptions must not be null!");
return new DefaultStreamMessageListenerContainerX<>(connectionFactory, options);

View File

@ -27,6 +27,20 @@ public class LambdaQueryWrapperX<T> extends LambdaQueryWrapper<T> {
return this;
}
public LambdaQueryWrapperX<T> likeRightIfPresent(SFunction<T, ?> column, String val) {
if (StringUtils.hasText(val)) {
return (LambdaQueryWrapperX<T>) super.likeRight(column, val);
}
return this;
}
public LambdaQueryWrapperX<T> likeLeftIfPresent(SFunction<T, ?> column, String val) {
if (StringUtils.hasText(val)) {
return (LambdaQueryWrapperX<T>) super.likeLeft(column, val);
}
return this;
}
public LambdaQueryWrapperX<T> inIfPresent(SFunction<T, ?> column, Collection<?> values) {
if (!CollectionUtils.isEmpty(values)) {
return (LambdaQueryWrapperX<T>) super.in(column, values);

View File

@ -13,7 +13,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 字段字段的 TypeHandler 实现类,基于 {@link AES} 实现
* 字段字段的 TypeHandler 实现类,基于 {@link cn.hutool.crypto.symmetric.AES} 实现
* 可通过 jasypt.encryptor.password 配置项,设置密钥
*
* @author yshop

View File

@ -1,5 +1,7 @@
package co.yixiang.yshop.framework.redis.config;
import co.yixiang.yshop.framework.redis.util.RedissonUtil;
import org.redisson.api.RedissonClient;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
@ -30,4 +32,14 @@ public class YshopRedisAutoConfiguration {
return template;
}
/**
* /
* @param redissonClient /
* @return Redisson 操作工具类
*/
@Bean
public RedissonUtil redissonUtil(RedissonClient redissonClient){
return new RedissonUtil(redissonClient);
}
}

View File

@ -0,0 +1,87 @@
package co.yixiang.yshop.framework.redis.util;
import co.yixiang.yshop.framework.common.exception.ErrorCode;
import co.yixiang.yshop.framework.common.exception.util.ServiceExceptionUtil;
import org.redisson.api.RBlockingDeque;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
/**
* @author pepis
* @apiNote Redisson 操作工具类
**/
public class RedissonUtil {
private final RedissonClient redissonClient;
Logger logger = LoggerFactory.getLogger(RedissonUtil.class);
public RedissonUtil(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
/**
* 向指定key的延时队列中添加任务
* @param key 延时队列
* @param v 订单id
* @param delay 延时时长
* @param timeUnit 延时单位
* @param <V> id类型
*/
public <V> void delayedOffer(String key,V v,long delay, TimeUnit timeUnit){
try {
RBlockingDeque<Object> blockingDeque = redissonClient.getBlockingDeque(key);
RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
delayedQueue.offer(v, delay, timeUnit);
String s = delay + timeUnit.toString();
logger.info("添加延时队列成功 ,延迟时间:" + s + "订单id: " + v);
} catch (Exception e) {
logger.error(e.getMessage());
}
}
/**
* 向指定key的延时队列中添加任务
* @param key 延时队列
* @param v 订单id
* @param delay 延时时长
* @param timeUnit 延时单位
* @param code 添加失败抛出异常
* @param <V> id类型
*/
public <V> void delayedOfferThrow(String key, V v, long delay, TimeUnit timeUnit, ErrorCode code){
try {
RBlockingDeque<Object> blockingDeque = redissonClient.getBlockingDeque(key);
RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
delayedQueue.offer(v, delay, timeUnit);
String s = delay + timeUnit.toString();
logger.info("添加延时队列成功 ,延迟时间:" + s + "订单id: " + v);
} catch (Exception e) {
logger.error(e.getMessage());
throw ServiceExceptionUtil.exception(code,e.getMessage());
}
}
/**
* 向指定key的延时队列中删除任务
* @param key 延时队列
* @param v 订单id
* @param code 添加失败抛出异常
* @param <V> id类型
*/
public <V> void delayedRemoveThrow(String key, V v, ErrorCode code){
try {
RBlockingDeque<Object> blockingDeque = redissonClient.getBlockingDeque(key);
RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
delayedQueue.remove(v);
logger.info("删除延时队列成功 订单id: " + v);
} catch (Exception e) {
logger.error(e.getMessage());
throw ServiceExceptionUtil.exception(code,e.getMessage());
}
}
}

View File

@ -186,4 +186,4 @@ public class YshopWebSecurityConfigurerAdapter {
return result;
}
}
}