parent
6c7599acfc
commit
1c25927a1f
@ -0,0 +1,6 @@ |
||||
/target/ |
||||
/.idea/ |
||||
*.iml |
||||
.mvn |
||||
mvnw |
||||
*.cmd |
||||
@ -0,0 +1,20 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<parent> |
||||
<artifactId>jxc</artifactId> |
||||
<groupId>cn.toesbieya</groupId> |
||||
<version>0.0.1-SNAPSHOT</version> |
||||
</parent> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
|
||||
<artifactId>api</artifactId> |
||||
|
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>cn.toesbieya</groupId> |
||||
<artifactId>common</artifactId> |
||||
</dependency> |
||||
</dependencies> |
||||
</project> |
||||
@ -0,0 +1,20 @@ |
||||
package cn.toesbieya.jxc.api.service; |
||||
|
||||
import cn.toesbieya.jxc.api.vo.AttachmentOperation; |
||||
import cn.toesbieya.jxc.common.model.entity.RecAttachment; |
||||
import cn.toesbieya.jxc.common.model.entity.RecLoginHistory; |
||||
import cn.toesbieya.jxc.common.model.entity.RecUserAction; |
||||
|
||||
import java.util.List; |
||||
|
||||
public interface RecordApi { |
||||
List<RecAttachment> getAttachmentByPid(String pid); |
||||
|
||||
void handleAttachment(AttachmentOperation vo); |
||||
|
||||
void delAttachmentByPid(String pid); |
||||
|
||||
void insertLoginHistory(RecLoginHistory vo); |
||||
|
||||
void insertUserAction(RecUserAction vo); |
||||
} |
||||
@ -0,0 +1,18 @@ |
||||
package cn.toesbieya.jxc.api.service; |
||||
|
||||
import cn.toesbieya.jxc.common.model.entity.BizStock; |
||||
import cn.toesbieya.jxc.common.model.vo.StockOutboundVo; |
||||
import cn.toesbieya.jxc.common.model.vo.StockSearch; |
||||
import cn.toesbieya.jxc.common.model.vo.StockSearchResult; |
||||
|
||||
import java.util.List; |
||||
|
||||
public interface StockApi { |
||||
List<StockSearchResult> getByCondition(StockSearch vo); |
||||
|
||||
List<BizStock> getDetail(String cids); |
||||
|
||||
void inbound(List<BizStock> list); |
||||
|
||||
void outbound(List<StockOutboundVo> list); |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
package cn.toesbieya.jxc.api.service.system; |
||||
|
||||
import cn.toesbieya.jxc.common.model.entity.SysRole; |
||||
import cn.toesbieya.jxc.common.model.vo.ResourceVo; |
||||
|
||||
import java.util.List; |
||||
|
||||
public interface ResourceApi { |
||||
//获取角色的权限列表,包含admin权限
|
||||
List<ResourceVo> getResourceByRole(SysRole role); |
||||
} |
||||
@ -0,0 +1,7 @@ |
||||
package cn.toesbieya.jxc.api.service.system; |
||||
|
||||
import cn.toesbieya.jxc.common.model.entity.SysRole; |
||||
|
||||
public interface RoleApi { |
||||
SysRole getRoleById(Integer roleId); |
||||
} |
||||
@ -0,0 +1,18 @@ |
||||
package cn.toesbieya.jxc.api.vo; |
||||
|
||||
import cn.toesbieya.jxc.common.model.entity.RecAttachment; |
||||
import lombok.AllArgsConstructor; |
||||
import lombok.Data; |
||||
import lombok.NoArgsConstructor; |
||||
|
||||
import java.io.Serializable; |
||||
import java.util.List; |
||||
|
||||
@Data |
||||
@NoArgsConstructor |
||||
@AllArgsConstructor |
||||
public class AttachmentOperation implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private List<RecAttachment> uploadImageList; |
||||
private List<String> deleteImageList; |
||||
} |
||||
@ -0,0 +1,45 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<parent> |
||||
<artifactId>jxc</artifactId> |
||||
<groupId>cn.toesbieya</groupId> |
||||
<version>0.0.1-SNAPSHOT</version> |
||||
</parent> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
|
||||
<artifactId>common</artifactId> |
||||
|
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-data-redis</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.apache.commons</groupId> |
||||
<artifactId>commons-pool2</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>javax.servlet</groupId> |
||||
<artifactId>javax.servlet-api</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.google.guava</groupId> |
||||
<artifactId>guava</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.yaml</groupId> |
||||
<artifactId>snakeyaml</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.alibaba</groupId> |
||||
<artifactId>fastjson</artifactId> |
||||
</dependency> |
||||
</dependencies> |
||||
</project> |
||||
@ -0,0 +1,19 @@ |
||||
package cn.toesbieya.jxc.common.config; |
||||
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature; |
||||
import com.alibaba.fastjson.support.config.FastJsonConfig; |
||||
|
||||
public class FastJsonConfigFactory { |
||||
public static FastJsonConfig defaultConfig() { |
||||
FastJsonConfig config = new FastJsonConfig(); |
||||
|
||||
config.setSerializerFeatures( |
||||
SerializerFeature.WriteMapNullValue, // 是否输出值为null的字段
|
||||
SerializerFeature.WriteDateUseDateFormat, |
||||
SerializerFeature.WriteNullListAsEmpty, |
||||
SerializerFeature.DisableCircularReferenceDetect // 禁用循环引用
|
||||
); |
||||
|
||||
return config; |
||||
} |
||||
} |
||||
@ -0,0 +1,34 @@ |
||||
package cn.toesbieya.jxc.common.config; |
||||
|
||||
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.data.redis.connection.RedisConnectionFactory; |
||||
import org.springframework.data.redis.core.RedisTemplate; |
||||
import org.springframework.data.redis.serializer.StringRedisSerializer; |
||||
|
||||
@Configuration |
||||
public class RedisConfig { |
||||
@Bean |
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { |
||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); |
||||
redisTemplate.setConnectionFactory(factory); |
||||
|
||||
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class); |
||||
fastJsonRedisSerializer.setFastJsonConfig(FastJsonConfigFactory.defaultConfig()); |
||||
|
||||
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); |
||||
|
||||
// 设置值(value)的序列化采用FastJsonRedisSerializer。
|
||||
redisTemplate.setValueSerializer(fastJsonRedisSerializer); |
||||
redisTemplate.setHashValueSerializer(fastJsonRedisSerializer); |
||||
|
||||
// 设置键(key)的序列化采用StringRedisSerializer。
|
||||
redisTemplate.setKeySerializer(stringRedisSerializer); |
||||
redisTemplate.setHashKeySerializer(stringRedisSerializer); |
||||
|
||||
redisTemplate.setDefaultSerializer(fastJsonRedisSerializer); |
||||
redisTemplate.afterPropertiesSet(); |
||||
return redisTemplate; |
||||
} |
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
package cn.toesbieya.jxc.common.constant; |
||||
|
||||
public class DocumentConstant { |
||||
//更新单据ID时的redis锁的键
|
||||
public static final String UPDATE_DOCUMENTS_LOCK_KEY = "UPDATE_DOCUMENTS"; |
||||
|
||||
//单据ID的redis缓存的键
|
||||
public static final String DOCUMENT_TYPE_REDIS_KEY = "documentsID"; |
||||
|
||||
//单据类型
|
||||
public static final String[] DOCUMENT_TYPE = {"CGDD", "CGRK", "CGTH", "XSDD", "XSCK", "XSTH"}; |
||||
} |
||||
@ -0,0 +1,18 @@ |
||||
package cn.toesbieya.jxc.common.constant; |
||||
|
||||
public class SessionConstant { |
||||
//session过期时间,单位秒
|
||||
public static long EXPIRE = 3600 * 8; |
||||
|
||||
//存储用户信息的键的前缀
|
||||
public static String REDIS_NAMESPACE = "sessions:"; |
||||
|
||||
//key过期事件的前缀
|
||||
public static String REDIS_EXPIRE_TOPIC_PREFIX = "__keyspace@0__:"; |
||||
|
||||
//http headers中token的字段名
|
||||
public static String TOKEN_KEY = "X-Token"; |
||||
|
||||
//区别无活动用户的时间间隔,单位秒
|
||||
public static long NO_ACTION_INTERVAL = 1800; |
||||
} |
||||
@ -0,0 +1,23 @@ |
||||
package cn.toesbieya.jxc.common.constant; |
||||
|
||||
public class SocketConstant { |
||||
//web-socket服务端订阅的redis主题名称
|
||||
public static String REDIS_EVENT_TOPIC_SEND = "socket-event-send"; |
||||
|
||||
//指定用户
|
||||
public static int REDIS_EVENT_SPECIFIC = 0; |
||||
//广播
|
||||
public static int REDIS_EVENT_BROADCAST = 1; |
||||
//登出
|
||||
public static int REDIS_EVENT_LOGOUT = 2; |
||||
|
||||
//存放于redis中的在线用户的ID集合的键
|
||||
public static String REDIS_ONLINE_USER = "socket:online-user"; |
||||
//存放于redis中的离线用户的离线时间的键
|
||||
public static String REDIS_OFFLINE_USER = "socket:offline-user"; |
||||
|
||||
//登出事件
|
||||
public static final String EVENT_LOGOUT = "logout"; |
||||
//新消息事件
|
||||
public static final String EVENT_NEW_MESSAGE = "new-message"; |
||||
} |
||||
@ -0,0 +1,15 @@ |
||||
package cn.toesbieya.jxc.common.enumeration; |
||||
|
||||
public enum GeneralStatusEnum { |
||||
DISABLED(0), ENABLED(1); |
||||
|
||||
private final int code; |
||||
|
||||
GeneralStatusEnum(int code) { |
||||
this.code = code; |
||||
} |
||||
|
||||
public int getCode() { |
||||
return code; |
||||
} |
||||
} |
||||
@ -0,0 +1,7 @@ |
||||
package cn.toesbieya.jxc.common.exception; |
||||
|
||||
public class JsonResultException extends RuntimeException { |
||||
public JsonResultException(String message) { |
||||
super(message); |
||||
} |
||||
} |
||||
@ -0,0 +1,33 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
import lombok.NoArgsConstructor; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
@NoArgsConstructor |
||||
public class BizDoc implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private String id; |
||||
private Integer cid; |
||||
private String cname; |
||||
private Long ctime; |
||||
private Integer vid; |
||||
private String vname; |
||||
private Long vtime; |
||||
private Integer status; |
||||
private String remark; |
||||
|
||||
public BizDoc(BizDoc doc) { |
||||
this.id = doc.getId(); |
||||
this.cid = doc.getCid(); |
||||
this.cname = doc.getCname(); |
||||
this.ctime = doc.getCtime(); |
||||
this.vid = doc.getVid(); |
||||
this.vname = doc.getVname(); |
||||
this.vtime = doc.getVtime(); |
||||
this.status = doc.getStatus(); |
||||
this.remark = doc.getRemark(); |
||||
} |
||||
} |
||||
@ -0,0 +1,25 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.AllArgsConstructor; |
||||
import lombok.Builder; |
||||
import lombok.Data; |
||||
import lombok.NoArgsConstructor; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
@Builder |
||||
@AllArgsConstructor |
||||
@NoArgsConstructor |
||||
public class BizDocHistory implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer id; |
||||
private String pid; |
||||
private Integer type; |
||||
private Integer uid; |
||||
private String uname; |
||||
private Integer status_before; |
||||
private Integer status_after; |
||||
private Long time; |
||||
private String info; |
||||
} |
||||
@ -0,0 +1,16 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
import java.math.BigDecimal; |
||||
|
||||
@Data |
||||
public class BizDocSub implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer id; |
||||
private String pid; |
||||
private Integer cid; |
||||
private String cname; |
||||
private BigDecimal num; |
||||
} |
||||
@ -0,0 +1,19 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.ToString; |
||||
|
||||
@Data |
||||
@ToString(callSuper = true) |
||||
@EqualsAndHashCode(callSuper = true) |
||||
@NoArgsConstructor |
||||
public class BizPurchaseInbound extends BizDoc { |
||||
private String pid; |
||||
|
||||
public BizPurchaseInbound(BizPurchaseInbound obj) { |
||||
super(obj); |
||||
this.pid = obj.getPid(); |
||||
} |
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
import lombok.ToString; |
||||
|
||||
@Data |
||||
@ToString(callSuper = true) |
||||
@EqualsAndHashCode(callSuper = true) |
||||
public class BizPurchaseInboundSub extends BizDocSub { |
||||
|
||||
} |
||||
@ -0,0 +1,29 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.ToString; |
||||
|
||||
import java.math.BigDecimal; |
||||
|
||||
@Data |
||||
@ToString(callSuper = true) |
||||
@EqualsAndHashCode(callSuper = true) |
||||
@NoArgsConstructor |
||||
public class BizPurchaseOrder extends BizDoc { |
||||
private Integer sid; |
||||
private String sname; |
||||
private Integer finish; |
||||
private Long ftime; |
||||
private BigDecimal total; |
||||
|
||||
public BizPurchaseOrder(BizPurchaseOrder obj) { |
||||
super(obj); |
||||
this.sid = obj.getSid(); |
||||
this.sname = obj.getSname(); |
||||
this.finish = obj.getFinish(); |
||||
this.ftime = obj.getFtime(); |
||||
this.total = obj.getTotal(); |
||||
} |
||||
} |
||||
@ -0,0 +1,15 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
import lombok.ToString; |
||||
|
||||
import java.math.BigDecimal; |
||||
|
||||
@Data |
||||
@ToString(callSuper = true) |
||||
@EqualsAndHashCode(callSuper = true) |
||||
public class BizPurchaseOrderSub extends BizDocSub { |
||||
private BigDecimal price; |
||||
private BigDecimal remain_num; |
||||
} |
||||
@ -0,0 +1,29 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.ToString; |
||||
|
||||
import java.math.BigDecimal; |
||||
|
||||
@Data |
||||
@ToString(callSuper = true) |
||||
@EqualsAndHashCode(callSuper = true) |
||||
@NoArgsConstructor |
||||
public class BizSellOrder extends BizDoc { |
||||
private Integer customer_id; |
||||
private String customer_name; |
||||
private Integer finish; |
||||
private Long ftime; |
||||
private BigDecimal total; |
||||
|
||||
public BizSellOrder(BizSellOrder obj) { |
||||
super(obj); |
||||
this.customer_id = obj.getCustomer_id(); |
||||
this.customer_name = obj.getCustomer_name(); |
||||
this.finish = obj.getFinish(); |
||||
this.ftime = obj.getFtime(); |
||||
this.total = obj.getTotal(); |
||||
} |
||||
} |
||||
@ -0,0 +1,15 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
import lombok.ToString; |
||||
|
||||
import java.math.BigDecimal; |
||||
|
||||
@Data |
||||
@ToString(callSuper = true) |
||||
@EqualsAndHashCode(callSuper = true) |
||||
public class BizSellOrderSub extends BizDocSub { |
||||
private BigDecimal price; |
||||
private BigDecimal remain_num; |
||||
} |
||||
@ -0,0 +1,19 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.ToString; |
||||
|
||||
@Data |
||||
@ToString(callSuper = true) |
||||
@EqualsAndHashCode(callSuper = true) |
||||
@NoArgsConstructor |
||||
public class BizSellOutbound extends BizDoc { |
||||
private String pid; |
||||
|
||||
public BizSellOutbound(BizSellOutbound obj) { |
||||
super(obj); |
||||
this.pid = obj.getPid(); |
||||
} |
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
import lombok.ToString; |
||||
|
||||
@Data |
||||
@ToString(callSuper = true) |
||||
@EqualsAndHashCode(callSuper = true) |
||||
public class BizSellOutboundSub extends BizDocSub { |
||||
private Integer sid; |
||||
} |
||||
@ -0,0 +1,25 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.AllArgsConstructor; |
||||
import lombok.Builder; |
||||
import lombok.Data; |
||||
import lombok.NoArgsConstructor; |
||||
|
||||
import java.io.Serializable; |
||||
import java.math.BigDecimal; |
||||
|
||||
@Data |
||||
@Builder |
||||
@NoArgsConstructor |
||||
@AllArgsConstructor |
||||
public class BizStock implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer id; |
||||
private Integer cid; |
||||
private String cname; |
||||
private BigDecimal num; |
||||
private BigDecimal price; |
||||
private Long ctime; |
||||
private String cgrkid; |
||||
private String cgddid; |
||||
} |
||||
@ -0,0 +1,26 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
public class Msg implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer id; |
||||
private String title; |
||||
private String content; |
||||
private Integer type; |
||||
private Integer cid; |
||||
private String cname; |
||||
private Long ctime; |
||||
private Integer pid; |
||||
private String pname; |
||||
private Long ptime; |
||||
private Integer wid; |
||||
private String wname; |
||||
private Long wtime; |
||||
private Integer status; |
||||
private Integer broadcast; |
||||
private String recipient; |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
public class MsgState implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer id; |
||||
private Integer mid; |
||||
private Integer uid; |
||||
private Long time; |
||||
} |
||||
@ -0,0 +1,23 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.AllArgsConstructor; |
||||
import lombok.Builder; |
||||
import lombok.Data; |
||||
import lombok.NoArgsConstructor; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
@Builder |
||||
@NoArgsConstructor |
||||
@AllArgsConstructor |
||||
public class RecAttachment implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer id; |
||||
private String pid; |
||||
private String name; |
||||
private Long time; |
||||
private Integer sort; |
||||
private String url; |
||||
private Long size; |
||||
} |
||||
@ -0,0 +1,23 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.AllArgsConstructor; |
||||
import lombok.Builder; |
||||
import lombok.Data; |
||||
import lombok.NoArgsConstructor; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
@Builder |
||||
@AllArgsConstructor |
||||
@NoArgsConstructor |
||||
public class RecLoginHistory implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Long id; |
||||
private Integer uid; |
||||
private String uname; |
||||
private String ip; |
||||
private String address; |
||||
private Integer type; |
||||
private Long time; |
||||
} |
||||
@ -0,0 +1,25 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.AllArgsConstructor; |
||||
import lombok.Builder; |
||||
import lombok.Data; |
||||
import lombok.NoArgsConstructor; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
@Builder |
||||
@AllArgsConstructor |
||||
@NoArgsConstructor |
||||
public class RecUserAction implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Long id; |
||||
private Integer uid; |
||||
private String uname; |
||||
private String url; |
||||
private String ip; |
||||
private Long time; |
||||
private String action; |
||||
private String error; |
||||
private Integer type; |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
public class StatFinishOrder implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer id; |
||||
private Integer purchase; |
||||
private Integer sell; |
||||
private Long time; |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
import lombok.ToString; |
||||
|
||||
@Data |
||||
@ToString(callSuper = true) |
||||
@EqualsAndHashCode(callSuper = true) |
||||
public class StatProfitGoods extends StatProfitTotal { |
||||
private Integer cid; |
||||
private String cname; |
||||
} |
||||
@ -0,0 +1,16 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
import java.math.BigDecimal; |
||||
|
||||
@Data |
||||
public class StatProfitTotal implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer id; |
||||
private BigDecimal purchase; |
||||
private BigDecimal sell; |
||||
private BigDecimal profit; |
||||
private Long time; |
||||
} |
||||
@ -0,0 +1,15 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
public class SysCategory implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer id; |
||||
private Integer pid; |
||||
private String name; |
||||
private Integer type; |
||||
private Long ctime; |
||||
} |
||||
@ -0,0 +1,19 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
public class SysCustomer implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer id; |
||||
private String name; |
||||
private String address; |
||||
private String linkman; |
||||
private String linkphone; |
||||
private String region; |
||||
private Integer status; |
||||
private Long ctime; |
||||
private String remark; |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
public class SysDepartment implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer id; |
||||
private Integer pid; |
||||
private String name; |
||||
private Integer status; |
||||
} |
||||
@ -0,0 +1,15 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
public class SysRegion implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private String id; |
||||
private String pid; |
||||
private String name; |
||||
private String fullname; |
||||
private int level; |
||||
} |
||||
@ -0,0 +1,17 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
public class SysResource implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer id; |
||||
private Integer pid; |
||||
private String name; |
||||
private String url; |
||||
private Integer admin; |
||||
private Long total_rate; |
||||
private Long ip_rate; |
||||
} |
||||
@ -0,0 +1,17 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
public class SysRole implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer id; |
||||
private String name; |
||||
private String resource_id; |
||||
private Integer cid; |
||||
private String cname; |
||||
private Long ctime; |
||||
private Integer status; |
||||
} |
||||
@ -0,0 +1,19 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
public class SysSupplier implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer id; |
||||
private String name; |
||||
private String address; |
||||
private String linkman; |
||||
private String linkphone; |
||||
private String region; |
||||
private Integer status; |
||||
private Long ctime; |
||||
private String remark; |
||||
} |
||||
@ -0,0 +1,22 @@ |
||||
package cn.toesbieya.jxc.common.model.entity; |
||||
|
||||
import lombok.AllArgsConstructor; |
||||
import lombok.Data; |
||||
import lombok.NoArgsConstructor; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
@AllArgsConstructor |
||||
@NoArgsConstructor |
||||
public class SysUser implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer id; |
||||
private String name; |
||||
private String pwd; |
||||
private Integer role; |
||||
private String avatar; |
||||
private Long ctime; |
||||
private Integer admin; |
||||
private Integer status; |
||||
} |
||||
@ -0,0 +1,9 @@ |
||||
package cn.toesbieya.jxc.common.model.vo; |
||||
|
||||
import lombok.Data; |
||||
|
||||
@Data |
||||
public class BaseSearch { |
||||
private Integer page; |
||||
private Integer pageSize; |
||||
} |
||||
@ -0,0 +1,23 @@ |
||||
package cn.toesbieya.jxc.common.model.vo; |
||||
|
||||
import cn.toesbieya.jxc.common.model.entity.SysResource; |
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.ToString; |
||||
|
||||
@Data |
||||
@NoArgsConstructor |
||||
@ToString(callSuper = true) |
||||
@EqualsAndHashCode(callSuper = true) |
||||
public class ResourceVo extends SysResource { |
||||
private String fullName; |
||||
|
||||
public ResourceVo(SysResource parent) { |
||||
this.setId(parent.getId()); |
||||
this.setPid(parent.getPid()); |
||||
this.setName(parent.getName()); |
||||
this.setUrl(parent.getUrl()); |
||||
this.setAdmin(parent.getAdmin()); |
||||
} |
||||
} |
||||
@ -0,0 +1,83 @@ |
||||
package cn.toesbieya.jxc.common.model.vo; |
||||
|
||||
import lombok.AllArgsConstructor; |
||||
import lombok.Builder; |
||||
import lombok.Data; |
||||
import lombok.NoArgsConstructor; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
@AllArgsConstructor |
||||
@NoArgsConstructor |
||||
@Builder |
||||
public class Result implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
|
||||
//返回码,200表示成功,其他表示失败
|
||||
private int status; |
||||
|
||||
//返回的信息
|
||||
private String msg; |
||||
|
||||
//返回的数据
|
||||
private Object data; |
||||
|
||||
public Result(int status) { |
||||
this.status = status; |
||||
} |
||||
|
||||
public Result(int status, String msg) { |
||||
this.status = status; |
||||
this.msg = msg; |
||||
} |
||||
|
||||
public Result(int status, Object data) { |
||||
this.status = status; |
||||
this.data = data; |
||||
} |
||||
|
||||
public static Result success() { |
||||
return new Result(200); |
||||
} |
||||
|
||||
public static Result success(String msg) { |
||||
return new Result(200, msg); |
||||
} |
||||
|
||||
public static Result success(Object data) { |
||||
return new Result(200, data); |
||||
} |
||||
|
||||
public static Result success(String msg, Object data) { |
||||
return new Result(200, msg, data); |
||||
} |
||||
|
||||
public static Result fail(String msg) { |
||||
return new Result(500, msg); |
||||
} |
||||
|
||||
public static Result notfound() { |
||||
return new Result(404, "请求地址不存在"); |
||||
} |
||||
|
||||
public static Result requireLogin() { |
||||
return new Result(401, "请登录后重试"); |
||||
} |
||||
|
||||
public static Result noPermission() { |
||||
return new Result(403, "没有权限进行该操作"); |
||||
} |
||||
|
||||
public static Result overload() { |
||||
return new Result(503, "当前请求的人数过多,请稍后重试"); |
||||
} |
||||
|
||||
public static Result tooManyRequest() { |
||||
return new Result(429, "操作过于频繁,请稍后重试"); |
||||
} |
||||
|
||||
public boolean isSuccess() { |
||||
return this.status == 200; |
||||
} |
||||
} |
||||
@ -0,0 +1,21 @@ |
||||
package cn.toesbieya.jxc.common.model.vo; |
||||
|
||||
import cn.toesbieya.jxc.common.constant.SocketConstant; |
||||
import lombok.Data; |
||||
|
||||
import java.util.List; |
||||
|
||||
@Data |
||||
public class SocketEventVo { |
||||
//事件类型
|
||||
private int type = SocketConstant.REDIS_EVENT_SPECIFIC; |
||||
|
||||
//事件名称
|
||||
private String event; |
||||
|
||||
//发送对象的id集合
|
||||
private List<Integer> to; |
||||
|
||||
//数据包
|
||||
private Object data; |
||||
} |
||||
@ -0,0 +1,16 @@ |
||||
package cn.toesbieya.jxc.common.model.vo; |
||||
|
||||
import lombok.AllArgsConstructor; |
||||
import lombok.Data; |
||||
import lombok.NoArgsConstructor; |
||||
|
||||
@Data |
||||
@NoArgsConstructor |
||||
@AllArgsConstructor |
||||
public class SocketOfflineVo { |
||||
//用户的sessionKey
|
||||
private String key; |
||||
|
||||
//断开时间
|
||||
private Long time; |
||||
} |
||||
@ -0,0 +1,17 @@ |
||||
package cn.toesbieya.jxc.common.model.vo; |
||||
|
||||
import lombok.AllArgsConstructor; |
||||
import lombok.Data; |
||||
import lombok.NoArgsConstructor; |
||||
|
||||
import java.io.Serializable; |
||||
import java.math.BigDecimal; |
||||
|
||||
@Data |
||||
@NoArgsConstructor |
||||
@AllArgsConstructor |
||||
public class StockOutboundVo implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer id; |
||||
private BigDecimal num; |
||||
} |
||||
@ -0,0 +1,20 @@ |
||||
package cn.toesbieya.jxc.common.model.vo; |
||||
|
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
import lombok.ToString; |
||||
|
||||
import java.io.Serializable; |
||||
|
||||
@Data |
||||
@ToString(callSuper = true) |
||||
@EqualsAndHashCode(callSuper = true) |
||||
public class StockSearch extends BaseSearch implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private String ids; |
||||
private String cids; |
||||
private Long startTime; |
||||
private Long endTime; |
||||
private String cgddids; |
||||
private String cgrkids; |
||||
} |
||||
@ -0,0 +1,15 @@ |
||||
package cn.toesbieya.jxc.common.model.vo; |
||||
|
||||
import lombok.Data; |
||||
|
||||
import java.io.Serializable; |
||||
import java.math.BigDecimal; |
||||
|
||||
@Data |
||||
public class StockSearchResult implements Serializable { |
||||
private static final long serialVersionUID = 1L; |
||||
private Integer cid; |
||||
private String cname; |
||||
private BigDecimal total_num; |
||||
private BigDecimal total_price; |
||||
} |
||||
@ -0,0 +1,31 @@ |
||||
package cn.toesbieya.jxc.common.model.vo; |
||||
|
||||
import cn.toesbieya.jxc.common.model.entity.SysUser; |
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
import lombok.NoArgsConstructor; |
||||
import lombok.ToString; |
||||
|
||||
import java.util.Set; |
||||
|
||||
@Data |
||||
@NoArgsConstructor |
||||
@ToString(callSuper = true) |
||||
@EqualsAndHashCode(callSuper = true) |
||||
public class UserVo extends SysUser { |
||||
private String token; |
||||
private String role_name; |
||||
private boolean online = false; |
||||
private Set<Integer> resource_ids; |
||||
|
||||
public UserVo(SysUser parent) { |
||||
this.setId(parent.getId()); |
||||
this.setName(parent.getName()); |
||||
this.setPwd(parent.getPwd()); |
||||
this.setRole(parent.getRole()); |
||||
this.setAvatar(parent.getAvatar()); |
||||
this.setCtime(parent.getCtime()); |
||||
this.setAdmin(parent.getAdmin()); |
||||
this.setStatus(parent.getStatus()); |
||||
} |
||||
} |
||||
@ -0,0 +1,53 @@ |
||||
package cn.toesbieya.jxc.common.utils; |
||||
|
||||
import java.time.Instant; |
||||
import java.time.LocalDateTime; |
||||
import java.time.ZoneId; |
||||
import java.time.format.DateTimeFormatter; |
||||
import java.util.Calendar; |
||||
import java.util.Date; |
||||
|
||||
public class DateUtil { |
||||
public static final DateTimeFormatter defaultFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
||||
|
||||
public static String dateFormat() { |
||||
return dateFormat(null, null); |
||||
} |
||||
|
||||
public static String dateFormat(Long time) { |
||||
LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneId.systemDefault()); |
||||
return dateFormat(localDateTime, null); |
||||
} |
||||
|
||||
public static String dateFormat(LocalDateTime time) { |
||||
return dateFormat(time, null); |
||||
} |
||||
|
||||
public static String dateFormat(DateTimeFormatter formatter) { |
||||
return dateFormat(null, formatter); |
||||
} |
||||
|
||||
public static String dateFormat(LocalDateTime time, DateTimeFormatter formatter) { |
||||
LocalDateTime t = time == null ? LocalDateTime.now() : time; |
||||
DateTimeFormatter f = formatter == null ? defaultFormatter : formatter; |
||||
return t.format(f); |
||||
} |
||||
|
||||
//获取今日零点的时间戳
|
||||
public static long getTimestampNow() { |
||||
long currentTimestamps = System.currentTimeMillis(); |
||||
return currentTimestamps - (currentTimestamps + 60 * 60 * 8 * 1000) % (60 * 60 * 24 * 1000); |
||||
} |
||||
|
||||
//获取n天前零点的时间戳
|
||||
public static long getTimestampBeforeNow(int before) { |
||||
Calendar cal = Calendar.getInstance(); |
||||
cal.setTime(new Date()); |
||||
cal.add(Calendar.DATE, -before); |
||||
cal.set(Calendar.HOUR_OF_DAY, 0); |
||||
cal.set(Calendar.MINUTE, 0); |
||||
cal.set(Calendar.SECOND, 0); |
||||
cal.set(Calendar.MILLISECOND, 0); |
||||
return cal.getTime().getTime(); |
||||
} |
||||
} |
||||
@ -0,0 +1,176 @@ |
||||
package cn.toesbieya.jxc.common.utils; |
||||
|
||||
import com.google.common.io.ByteStreams; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.core.io.ClassPathResource; |
||||
import org.springframework.data.redis.core.*; |
||||
import org.springframework.data.redis.core.script.DefaultRedisScript; |
||||
import org.springframework.data.redis.core.script.RedisScript; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import java.io.Closeable; |
||||
import java.io.IOException; |
||||
import java.util.*; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
@Component |
||||
@Slf4j |
||||
public class RedisUtil { |
||||
private static String INCREMENT_AND_EXPIRE_SCRIPT; |
||||
private static RedisTemplate<String, Object> redisTemplate; |
||||
private static StringRedisTemplate stringRedisTemplate; |
||||
|
||||
static { |
||||
try { |
||||
INCREMENT_AND_EXPIRE_SCRIPT = new String(ByteStreams.toByteArray(new ClassPathResource("/script/increase_and_expire.lua").getInputStream())); |
||||
} |
||||
catch (IOException e) { |
||||
e.printStackTrace(); |
||||
} |
||||
} |
||||
|
||||
@Autowired |
||||
public RedisUtil(RedisTemplate<String, Object> redisTemplate, StringRedisTemplate stringRedisTemplate) { |
||||
RedisUtil.redisTemplate = redisTemplate; |
||||
RedisUtil.stringRedisTemplate = stringRedisTemplate; |
||||
} |
||||
|
||||
public static Set<String> keys(String match) { |
||||
return redisTemplate.execute((RedisCallback<Set<String>>) connection -> { |
||||
Set<String> result = new HashSet<>(); |
||||
|
||||
Cursor<byte[]> cursor = |
||||
connection.scan( |
||||
new ScanOptions |
||||
.ScanOptionsBuilder() |
||||
.match(match) |
||||
.count(2000) |
||||
.build() |
||||
); |
||||
|
||||
while (cursor.hasNext()) { |
||||
result.add(new String(cursor.next())); |
||||
} |
||||
|
||||
return result; |
||||
}); |
||||
} |
||||
|
||||
public static boolean exist(String key) { |
||||
Boolean result = redisTemplate.hasKey(key); |
||||
return result != null && result; |
||||
} |
||||
|
||||
public static void del(String... keys) { |
||||
redisTemplate.delete(Arrays.asList(keys)); |
||||
} |
||||
|
||||
public static long incrAndExpire(String key, int time) { |
||||
RedisScript<Long> redisScript = new DefaultRedisScript<>(INCREMENT_AND_EXPIRE_SCRIPT, Long.class); |
||||
|
||||
Long result = redisTemplate.execute(redisScript, Collections.singletonList(key), time); |
||||
|
||||
return result == null ? 0 : result; |
||||
} |
||||
|
||||
public static Object get(String key) { |
||||
return redisTemplate.opsForValue().get(key); |
||||
} |
||||
|
||||
public static void set(String key, Object value) { |
||||
redisTemplate.opsForValue().set(key, value); |
||||
} |
||||
|
||||
public static void set(String key, Object value, long expire) { |
||||
redisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS); |
||||
} |
||||
|
||||
public static Object hget(String key, String field) { |
||||
return redisTemplate.opsForHash().get(key, field); |
||||
} |
||||
|
||||
public static Map<Object, Object> hgetAll(String key) { |
||||
return redisTemplate.opsForHash().entries(key); |
||||
} |
||||
|
||||
public static void hset(String key, String field, Object value) { |
||||
redisTemplate.opsForHash().put(key, field, value); |
||||
} |
||||
|
||||
public static void hdel(String key, Object... fields) { |
||||
redisTemplate.opsForHash().delete(key, fields); |
||||
} |
||||
|
||||
public static List<Object> hmget(String key, String... field) { |
||||
return redisTemplate.opsForHash().multiGet(key, Arrays.asList(field)); |
||||
} |
||||
|
||||
public static void hmset(String key, Map<String, Object> map) { |
||||
redisTemplate.opsForHash().putAll(key, map); |
||||
} |
||||
|
||||
public static long sadd(String key, Object... members) { |
||||
Long result = redisTemplate.opsForSet().add(key, members); |
||||
return result == null ? 0 : result; |
||||
} |
||||
|
||||
public static void srem(String key, Object... members) { |
||||
redisTemplate.opsForSet().remove(key, members); |
||||
} |
||||
|
||||
public static long scard(String key) { |
||||
Long result = redisTemplate.opsForSet().size(key); |
||||
return result == null ? 0 : result; |
||||
} |
||||
|
||||
public static Set<Object> smembers(String key) { |
||||
Set<Object> result = redisTemplate.opsForSet().members(key); |
||||
return result == null ? Collections.emptySet() : result; |
||||
} |
||||
|
||||
public static <T> T execute(RedisScript<T> script, List<String> keys, Object... args) { |
||||
return redisTemplate.execute(script, keys, args); |
||||
} |
||||
|
||||
public static RedisTemplate<String, Object> getRedisTemplate() { |
||||
return redisTemplate; |
||||
} |
||||
|
||||
public static StringRedisTemplate getStringRedisTemplate() { |
||||
return stringRedisTemplate; |
||||
} |
||||
|
||||
public static class Locker implements Closeable { |
||||
private final String key; |
||||
private final String value; |
||||
private final long expire; |
||||
|
||||
public Locker(String key) { |
||||
this.key = "lock:" + key; |
||||
this.value = String.valueOf(System.currentTimeMillis()); |
||||
this.expire = 300; |
||||
} |
||||
|
||||
@Override |
||||
public void close() { |
||||
this.unlock(); |
||||
} |
||||
|
||||
public boolean lock() { |
||||
String LOCK_LUA_SCRIPT = "return redis.call('set',KEYS[1],ARGV[1],'EX',ARGV[2],'NX')"; |
||||
RedisScript<Boolean> redisScript = new DefaultRedisScript<>(LOCK_LUA_SCRIPT, Boolean.class); |
||||
Boolean result = redisTemplate.execute(redisScript, Collections.singletonList(key), value, expire); |
||||
return result != null; |
||||
} |
||||
|
||||
public void unlock() { |
||||
String UNLOCK_LUA_SCRIPT = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return false end"; |
||||
RedisScript<Boolean> redisScript = new DefaultRedisScript<>(UNLOCK_LUA_SCRIPT, Boolean.class); |
||||
Boolean result = redisTemplate.execute(redisScript, Collections.singletonList(key), value); |
||||
if (result == null || !result) { |
||||
log.error("redis锁释放失败,key:{},value:{}", key, value); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,57 @@ |
||||
package cn.toesbieya.jxc.common.utils; |
||||
|
||||
import cn.toesbieya.jxc.common.model.entity.SysUser; |
||||
import cn.toesbieya.jxc.common.model.vo.UserVo; |
||||
import com.alibaba.fastjson.JSONObject; |
||||
import cn.toesbieya.jxc.common.constant.SessionConstant; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
public class SessionUtil { |
||||
//token中的连接符
|
||||
private static final String tokenSeparator = "-*-"; |
||||
|
||||
public static void remove(String token) { |
||||
if (StringUtils.isEmpty(token)) return; |
||||
|
||||
RedisUtil.del(SessionConstant.REDIS_NAMESPACE + token); |
||||
} |
||||
|
||||
public static void save(UserVo user) { |
||||
String token = user.getToken(); |
||||
|
||||
if (StringUtils.isEmpty(token)) return; |
||||
|
||||
RedisUtil.set(SessionConstant.REDIS_NAMESPACE + token, user, SessionConstant.EXPIRE); |
||||
} |
||||
|
||||
public static UserVo get(String token) { |
||||
if (StringUtils.isEmpty(token)) return null; |
||||
|
||||
JSONObject o = (JSONObject) RedisUtil.get(SessionConstant.REDIS_NAMESPACE + token); |
||||
|
||||
if (o == null) return null; |
||||
|
||||
return JSONObject.toJavaObject(o, UserVo.class); |
||||
} |
||||
|
||||
public static UserVo get(HttpServletRequest request) { |
||||
String token = request.getHeader(SessionConstant.TOKEN_KEY); |
||||
return get(token); |
||||
} |
||||
|
||||
public static String generateToken(SysUser user) { |
||||
return user.getId() + tokenSeparator + Util.UUID(); |
||||
} |
||||
|
||||
public static Integer getUidFromToken(String token) { |
||||
if (StringUtils.isEmpty(token)) { |
||||
return null; |
||||
} |
||||
|
||||
String[] arr = token.split(tokenSeparator); |
||||
|
||||
return arr.length > 1 ? Integer.valueOf(arr[0]) : null; |
||||
} |
||||
} |
||||
@ -0,0 +1,29 @@ |
||||
package cn.toesbieya.jxc.common.utils; |
||||
|
||||
import org.springframework.expression.EvaluationContext; |
||||
import org.springframework.expression.ExpressionParser; |
||||
import org.springframework.expression.spel.standard.SpelExpressionParser; |
||||
import org.springframework.expression.spel.support.StandardEvaluationContext; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
public class SpringUtil { |
||||
private static final ExpressionParser parser = new SpelExpressionParser(); |
||||
|
||||
/** |
||||
* el表达式解析 |
||||
* |
||||
* @param el 表达式 |
||||
* @param names 参数名称数组 |
||||
* @param args 参数数组 |
||||
*/ |
||||
public static Object spell(String el, String[] names, Object[] args) { |
||||
if (StringUtils.isEmpty(el)) { |
||||
return null; |
||||
} |
||||
EvaluationContext context = new StandardEvaluationContext(); |
||||
for (int i = 0; i < args.length; i++) { |
||||
context.setVariable(names[i], args[i]); |
||||
} |
||||
return parser.parseExpression(el).getValue(context); |
||||
} |
||||
} |
||||
@ -0,0 +1,72 @@ |
||||
package cn.toesbieya.jxc.common.utils; |
||||
|
||||
import org.springframework.util.StringUtils; |
||||
|
||||
import java.io.PrintWriter; |
||||
import java.io.StringWriter; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.UUID; |
||||
import java.util.function.Predicate; |
||||
import java.util.regex.Pattern; |
||||
import java.util.stream.Collectors; |
||||
|
||||
public class Util { |
||||
public static String UUID() { |
||||
return UUID.randomUUID().toString(); |
||||
} |
||||
|
||||
public static List<Integer> str2List(String string) { |
||||
if (StringUtils.isEmpty(string)) { |
||||
return Collections.emptyList(); |
||||
} |
||||
return Arrays.stream(string.split(",")) |
||||
.map(i -> Integer.parseInt(i.trim())) |
||||
.collect(Collectors.toList()); |
||||
} |
||||
|
||||
public static String str2Hex(String s) { |
||||
byte[] baKeyword = new byte[s.length() / 2]; |
||||
for (int i = 0; i < baKeyword.length; i++) { |
||||
baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16)); |
||||
} |
||||
return new String(baKeyword, StandardCharsets.ISO_8859_1); |
||||
} |
||||
|
||||
public static boolean isInteger(String str) { |
||||
Pattern pattern = Pattern.compile("^[-+]?[\\d]*$"); |
||||
return pattern.matcher(str).matches(); |
||||
} |
||||
|
||||
public static String exception2Str(Exception e) { |
||||
StringWriter stringWriter = new StringWriter(); |
||||
PrintWriter printWriter = new PrintWriter(stringWriter); |
||||
e.printStackTrace(printWriter); |
||||
printWriter.close(); |
||||
return stringWriter.toString(); |
||||
} |
||||
|
||||
public static <T> T find(Iterable<T> list, Predicate<T> predicate) { |
||||
for (T t : list) { |
||||
if (predicate.test(t)) return t; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public static <T> int findIndex(Iterable<T> list, Predicate<T> predicate) { |
||||
int i = -1; |
||||
if (list == null) return i; |
||||
for (T t : list) { |
||||
i++; |
||||
if (predicate.test(t)) return i; |
||||
} |
||||
return -1; |
||||
} |
||||
|
||||
public static <T> boolean some(Iterable<T> list, Predicate<T> predicate) { |
||||
int index = findIndex(list, predicate); |
||||
return index > -1; |
||||
} |
||||
} |
||||
@ -0,0 +1,36 @@ |
||||
package cn.toesbieya.jxc.common.utils; |
||||
|
||||
import cn.toesbieya.jxc.common.model.vo.SocketEventVo; |
||||
import cn.toesbieya.jxc.common.constant.SocketConstant; |
||||
|
||||
import java.util.List; |
||||
import java.util.Set; |
||||
import java.util.stream.Collectors; |
||||
|
||||
public class WebSocketUtil { |
||||
//获取在线用户的总数
|
||||
public static long getOnlineUserNum() { |
||||
return RedisUtil.scard(SocketConstant.REDIS_ONLINE_USER); |
||||
} |
||||
|
||||
//获取所有在线用户的id集合
|
||||
public static Set<Integer> getOnlineUserIds() { |
||||
Set<Object> result = RedisUtil.smembers(SocketConstant.REDIS_ONLINE_USER); |
||||
|
||||
return result.stream().map(e -> (Integer) e).collect(Collectors.toSet()); |
||||
} |
||||
|
||||
//发送消息
|
||||
public static void sendEvent(SocketEventVo vo) { |
||||
RedisUtil.getRedisTemplate().convertAndSend(SocketConstant.REDIS_EVENT_TOPIC_SEND, vo); |
||||
} |
||||
|
||||
//登出
|
||||
public static void sendLogoutEvent(List<Integer> to, String msg) { |
||||
SocketEventVo vo = new SocketEventVo(); |
||||
vo.setEvent(SocketConstant.EVENT_LOGOUT); |
||||
vo.setTo(to); |
||||
vo.setData(msg); |
||||
sendEvent(vo); |
||||
} |
||||
} |
||||
@ -0,0 +1,45 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<parent> |
||||
<artifactId>jxc</artifactId> |
||||
<groupId>cn.toesbieya</groupId> |
||||
<version>0.0.1-SNAPSHOT</version> |
||||
</parent> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
|
||||
<artifactId>gateway</artifactId> |
||||
|
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>cn.toesbieya</groupId> |
||||
<artifactId>common</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>mysql</groupId> |
||||
<artifactId>mysql-connector-java</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-jdbc</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.springframework.cloud</groupId> |
||||
<artifactId>spring-cloud-starter-gateway</artifactId> |
||||
</dependency> |
||||
|
||||
<!--<dependency> |
||||
<groupId>com.alibaba.csp</groupId> |
||||
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId> |
||||
</dependency>--> |
||||
</dependencies> |
||||
</project> |
||||
@ -0,0 +1,13 @@ |
||||
package cn.toesbieya.jxc.gateway; |
||||
|
||||
import org.springframework.boot.SpringApplication; |
||||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient; |
||||
|
||||
@SpringBootApplication(scanBasePackages = "cn.toesbieya.jxc") |
||||
@EnableDiscoveryClient |
||||
public class GatewayApplication { |
||||
public static void main(String[] args) { |
||||
SpringApplication.run(GatewayApplication.class, args); |
||||
} |
||||
} |
||||
@ -0,0 +1,19 @@ |
||||
package cn.toesbieya.jxc.gateway.config; |
||||
|
||||
import lombok.Data; |
||||
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import java.util.HashSet; |
||||
import java.util.Set; |
||||
|
||||
@Component |
||||
@ConfigurationProperties("spring.cloud.gateway") |
||||
@Data |
||||
public class GatewayConfig { |
||||
private Set<String> whitelist = new HashSet<>(); |
||||
|
||||
private String dataId = "jxc-gateway.yml"; |
||||
|
||||
private String group = "DEFAULT_GROUP"; |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
package cn.toesbieya.jxc.gateway.config; |
||||
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import reactor.core.publisher.Mono; |
||||
|
||||
@Configuration |
||||
public class RateConfig { |
||||
@Bean |
||||
public KeyResolver urlKeyResolver() { |
||||
return exchange -> Mono.just(exchange.getRequest().getPath().value()); |
||||
} |
||||
} |
||||
@ -0,0 +1,97 @@ |
||||
package cn.toesbieya.jxc.gateway.config; |
||||
|
||||
import cn.toesbieya.jxc.common.model.entity.SysResource; |
||||
import cn.toesbieya.jxc.common.model.vo.UserVo; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.boot.CommandLineRunner; |
||||
import org.springframework.jdbc.core.BeanPropertyRowMapper; |
||||
import org.springframework.jdbc.core.JdbcTemplate; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import java.time.Instant; |
||||
import java.time.temporal.ChronoUnit; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
|
||||
@Component |
||||
@Slf4j |
||||
public class ResourceConfig implements CommandLineRunner { |
||||
private final static ConcurrentHashMap<String, Integer> urlMap = new ConcurrentHashMap<>(256); |
||||
private final static ConcurrentHashMap<String, Integer> adminUrlMap = new ConcurrentHashMap<>(16); |
||||
private static JdbcTemplate jdbcTemplate; |
||||
|
||||
public static boolean authority(UserVo user, String url) { |
||||
//超级管理员放行
|
||||
if (user.getAdmin() == 1) { |
||||
return true; |
||||
} |
||||
//普通用户访问admin权限的资源时拦截
|
||||
if (adminUrlMap.containsKey(url)) { |
||||
return false; |
||||
} |
||||
//权限表中无记录的资源放行
|
||||
if (!urlMap.containsKey(url)) { |
||||
return true; |
||||
} |
||||
|
||||
Set<Integer> ids = user.getResource_ids(); |
||||
|
||||
return ids != null && ids.contains(urlMap.get(url)); |
||||
} |
||||
|
||||
@Override |
||||
public void run(String... args) throws Exception { |
||||
init(false); |
||||
} |
||||
|
||||
public static void init(boolean clear) { |
||||
Instant start = Instant.now(); |
||||
|
||||
if (clear) { |
||||
urlMap.clear(); |
||||
adminUrlMap.clear(); |
||||
} |
||||
|
||||
String sql = "select id,pid,url,admin from sys_resource"; |
||||
|
||||
List<SysResource> resources = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(SysResource.class)); |
||||
|
||||
completeNode(resources); |
||||
|
||||
for (SysResource p : resources) { |
||||
if (p.getAdmin().equals(1)) { |
||||
adminUrlMap.put(p.getUrl(), p.getId()); |
||||
} |
||||
else urlMap.put(p.getUrl(), p.getId()); |
||||
} |
||||
|
||||
Instant end = Instant.now(); |
||||
log.info("权限资源加载完成,耗时:{}毫秒", ChronoUnit.MILLIS.between(start, end)); |
||||
} |
||||
|
||||
@Autowired |
||||
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { |
||||
ResourceConfig.jdbcTemplate = jdbcTemplate; |
||||
} |
||||
|
||||
private static void completeNode(List<SysResource> list) { |
||||
HashMap<Integer, String> url = new HashMap<>(128); |
||||
|
||||
for (SysResource resource : list) { |
||||
//pid为0时跳过
|
||||
if (resource.getPid() == 0) { |
||||
url.put(resource.getId(), resource.getUrl()); |
||||
continue; |
||||
} |
||||
|
||||
//获取父节点进行拼接
|
||||
String parentUrl = url.get(resource.getPid()); |
||||
|
||||
resource.setUrl(parentUrl + resource.getUrl()); |
||||
url.put(resource.getId(), resource.getUrl()); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,138 @@ |
||||
package cn.toesbieya.jxc.gateway.config; |
||||
|
||||
import cn.toesbieya.jxc.gateway.filter.SecurityFilter; |
||||
import com.alibaba.nacos.api.NacosFactory; |
||||
import com.alibaba.nacos.api.PropertyKeyConst; |
||||
import com.alibaba.nacos.api.config.ConfigService; |
||||
import com.alibaba.nacos.api.config.listener.Listener; |
||||
import com.alibaba.nacos.api.exception.NacosException; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.beans.factory.annotation.Value; |
||||
import org.springframework.cloud.gateway.event.RefreshRoutesEvent; |
||||
import org.springframework.cloud.gateway.filter.FilterDefinition; |
||||
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; |
||||
import org.springframework.cloud.gateway.route.RouteDefinition; |
||||
import org.springframework.cloud.gateway.route.RouteDefinitionWriter; |
||||
import org.springframework.context.ApplicationEventPublisher; |
||||
import org.springframework.context.ApplicationEventPublisherAware; |
||||
import org.springframework.stereotype.Component; |
||||
import org.yaml.snakeyaml.Yaml; |
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import javax.annotation.PostConstruct; |
||||
import java.net.URI; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Properties; |
||||
import java.util.concurrent.Executor; |
||||
import java.util.stream.Collectors; |
||||
|
||||
@Component |
||||
@Slf4j |
||||
public class RouteConfig implements ApplicationEventPublisherAware { |
||||
@Value("${spring.cloud.nacos.config.server-addr}") |
||||
private String NACOS_SERVER_ADDR; |
||||
|
||||
private GatewayConfig config; |
||||
|
||||
private RouteDefinitionWriter writer; |
||||
|
||||
private ApplicationEventPublisher publisher; |
||||
|
||||
@PostConstruct |
||||
public void addListener() throws NacosException { |
||||
Properties properties = new Properties(); |
||||
properties.put(PropertyKeyConst.SERVER_ADDR, NACOS_SERVER_ADDR); |
||||
ConfigService configService = NacosFactory.createConfigService(properties); |
||||
|
||||
configService.addListener(config.getDataId(), config.getGroup(), new Listener() { |
||||
@Override |
||||
public Executor getExecutor() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
@SuppressWarnings("unchecked") |
||||
public void receiveConfigInfo(String configInfo) { |
||||
Yaml yaml = new Yaml(); |
||||
Map<String, Object> config = yaml.load(configInfo); |
||||
config = (Map<String, Object>) config.get("spring"); |
||||
config = (Map<String, Object>) config.get("cloud"); |
||||
config = (Map<String, Object>) config.get("gateway"); |
||||
|
||||
SecurityFilter.updateWhitelist((List<String>) config.get("whitelist")); |
||||
|
||||
List<Map<String, Object>> routes = (List<Map<String, Object>>) config.get("routes"); |
||||
|
||||
if (routes == null) return; |
||||
|
||||
List<RouteDefinition> list = |
||||
routes |
||||
.stream() |
||||
.map(map -> { |
||||
RouteDefinition route = new RouteDefinition(); |
||||
|
||||
route.setId((String) map.get("id")); |
||||
|
||||
route.setUri(URI.create((String) map.get("uri"))); |
||||
|
||||
List<String> predicates = (List<String>) map.get("predicates"); |
||||
|
||||
if (predicates != null) { |
||||
route.setPredicates( |
||||
predicates |
||||
.stream() |
||||
.map(PredicateDefinition::new) |
||||
.collect(Collectors.toList()) |
||||
); |
||||
} |
||||
|
||||
List<String> filters = (List<String>) map.get("filters"); |
||||
|
||||
if (filters != null) { |
||||
route.setFilters( |
||||
filters |
||||
.stream() |
||||
.map(FilterDefinition::new) |
||||
.collect(Collectors.toList()) |
||||
); |
||||
} |
||||
|
||||
return route; |
||||
}) |
||||
.collect(Collectors.toList()); |
||||
|
||||
update(list); |
||||
|
||||
//SentinelFilter.registerRules(list);
|
||||
} |
||||
}); |
||||
} |
||||
|
||||
private void update(List<RouteDefinition> routes) { |
||||
if (routes == null) return; |
||||
|
||||
routes.forEach(route -> { |
||||
this.writer.delete(Mono.just(route.getId())); |
||||
writer.save(Mono.just(route)).subscribe(); |
||||
}); |
||||
|
||||
this.publisher.publishEvent(new RefreshRoutesEvent(this)); |
||||
} |
||||
|
||||
@Override |
||||
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { |
||||
this.publisher = publisher; |
||||
} |
||||
|
||||
@Autowired |
||||
public void setWriter(RouteDefinitionWriter writer) { |
||||
this.writer = writer; |
||||
} |
||||
|
||||
@Autowired |
||||
public void setGatewayProperties(GatewayConfig config) { |
||||
this.config = config; |
||||
} |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
package cn.toesbieya.jxc.gateway.controller; |
||||
|
||||
import cn.toesbieya.jxc.common.model.vo.Result; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.ResponseBody; |
||||
|
||||
public class FallbackController { |
||||
@RequestMapping("fallback") |
||||
@ResponseBody |
||||
public Result fallback() { |
||||
return Result.fail("服务异常"); |
||||
} |
||||
} |
||||
@ -0,0 +1,106 @@ |
||||
package cn.toesbieya.jxc.gateway.filter; |
||||
|
||||
import cn.toesbieya.jxc.common.model.vo.UserVo; |
||||
import cn.toesbieya.jxc.gateway.config.GatewayConfig; |
||||
import cn.toesbieya.jxc.gateway.config.ResourceConfig; |
||||
import com.alibaba.fastjson.JSON; |
||||
import cn.toesbieya.jxc.common.constant.SessionConstant; |
||||
import cn.toesbieya.jxc.common.model.vo.Result; |
||||
import cn.toesbieya.jxc.common.utils.SessionUtil; |
||||
import cn.toesbieya.jxc.common.utils.Util; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.cloud.gateway.filter.GlobalFilter; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.core.annotation.Order; |
||||
import org.springframework.core.io.buffer.DataBuffer; |
||||
import org.springframework.http.HttpStatus; |
||||
import org.springframework.http.server.PathContainer; |
||||
import org.springframework.http.server.reactive.ServerHttpResponse; |
||||
import org.springframework.stereotype.Component; |
||||
import org.springframework.web.util.pattern.PathPattern; |
||||
import org.springframework.web.util.pattern.PathPatternParser; |
||||
import reactor.core.publisher.Mono; |
||||
|
||||
import java.nio.charset.StandardCharsets; |
||||
import java.util.ArrayList; |
||||
import java.util.Collection; |
||||
import java.util.List; |
||||
import java.util.stream.Collectors; |
||||
|
||||
import static org.springframework.http.server.PathContainer.parsePath; |
||||
|
||||
@Component |
||||
@Slf4j |
||||
public class SecurityFilter { |
||||
private GatewayConfig gatewayConfig; |
||||
|
||||
private static final PathPatternParser pathPatternParser = new PathPatternParser(); |
||||
|
||||
private static List<PathPattern> whitelistPatterns = new ArrayList<>(); |
||||
|
||||
@Bean |
||||
@Order(-1) |
||||
public GlobalFilter globalFilter() { |
||||
updateWhitelist(gatewayConfig.getWhitelist()); |
||||
|
||||
return ((exchange, chain) -> { |
||||
String requestUrl = exchange.getRequest().getURI().getPath(); |
||||
|
||||
PathContainer path = parsePath(requestUrl); |
||||
|
||||
//放行白名单内的url
|
||||
if (Util.some(whitelistPatterns, pattern -> pattern.matches(path))) { |
||||
return chain.filter(exchange); |
||||
} |
||||
|
||||
ServerHttpResponse response = exchange.getResponse(); |
||||
|
||||
String token = exchange.getRequest().getHeaders().getFirst(SessionConstant.TOKEN_KEY); |
||||
|
||||
UserVo user = SessionUtil.get(token); |
||||
|
||||
//未登录
|
||||
if (user == null) { |
||||
return responseJSON(response, Result.requireLogin()); |
||||
} |
||||
|
||||
//没有权限
|
||||
if (!ResourceConfig.authority(user, requestUrl)) { |
||||
return responseJSON(response, Result.noPermission()); |
||||
} |
||||
|
||||
return chain.filter(exchange); |
||||
}); |
||||
} |
||||
|
||||
private Mono<Void> responseJSON(ServerHttpResponse response, Result result) { |
||||
byte[] bytes = JSON.toJSONString(result).getBytes(StandardCharsets.UTF_8); |
||||
|
||||
DataBuffer buffer = response.bufferFactory().wrap(bytes); |
||||
|
||||
response.setStatusCode(HttpStatus.OK); |
||||
|
||||
response.getHeaders().add("Content-Type", "application/json; charset=utf-8"); |
||||
|
||||
return response.writeWith(Mono.just(buffer)); |
||||
} |
||||
|
||||
public static void updateWhitelist(Collection<String> list) { |
||||
if (list == null) { |
||||
whitelistPatterns = new ArrayList<>(); |
||||
return; |
||||
} |
||||
|
||||
whitelistPatterns = |
||||
list |
||||
.stream() |
||||
.map(pathPatternParser::parse) |
||||
.collect(Collectors.toList()); |
||||
} |
||||
|
||||
@Autowired |
||||
public void setGatewayProperties(GatewayConfig gatewayConfig) { |
||||
this.gatewayConfig = gatewayConfig; |
||||
} |
||||
} |
||||
@ -0,0 +1,68 @@ |
||||
package cn.toesbieya.jxc.gateway.filter; |
||||
|
||||
import org.springframework.context.annotation.Configuration; |
||||
|
||||
@Configuration |
||||
public class SentinelFilter { |
||||
/*private GatewayProperties gatewayProperties; |
||||
|
||||
private final List<ViewResolver> viewResolvers; |
||||
private final ServerCodecConfigurer serverCodecConfigurer; |
||||
|
||||
public SentinelFilter(ObjectProvider<List<ViewResolver>> viewResolversProvider, |
||||
ServerCodecConfigurer serverCodecConfigurer) { |
||||
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList); |
||||
this.serverCodecConfigurer = serverCodecConfigurer; |
||||
} |
||||
|
||||
@Bean |
||||
@Order(Ordered.HIGHEST_PRECEDENCE) |
||||
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() { |
||||
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer); |
||||
} |
||||
|
||||
@Bean |
||||
@Order(Ordered.HIGHEST_PRECEDENCE) |
||||
public GlobalFilter sentinelGatewayFilter() { |
||||
return new SentinelGatewayFilter(); |
||||
} |
||||
|
||||
@PostConstruct |
||||
public void init() { |
||||
registerRules(gatewayProperties.getRoutes()); |
||||
|
||||
GatewayCallbackManager.setBlockHandler( |
||||
(exchange, t) -> |
||||
ServerResponse |
||||
.status(HttpStatus.OK) |
||||
.contentType(MediaType.APPLICATION_JSON_UTF8) |
||||
.body(fromObject(Result.tooManyRequest())) |
||||
); |
||||
} |
||||
|
||||
public static void registerRules(List<RouteDefinition> routes) { |
||||
Set<GatewayFlowRule> rules = new HashSet<>(); |
||||
|
||||
routes.forEach(route -> { |
||||
String id = route.getId(); |
||||
|
||||
rules.add(new GatewayFlowRule(id).setCount(100)); |
||||
|
||||
rules.add( |
||||
new GatewayFlowRule(id) |
||||
.setCount(1) |
||||
.setParamItem( |
||||
new GatewayParamFlowItem() |
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP) |
||||
) |
||||
); |
||||
}); |
||||
|
||||
GatewayRuleManager.register2Property(new DynamicSentinelProperty<>(rules)); |
||||
} |
||||
|
||||
@Autowired |
||||
public void setGatewayProperties(GatewayProperties gatewayProperties) { |
||||
this.gatewayProperties = gatewayProperties; |
||||
}*/ |
||||
} |
||||
@ -0,0 +1,15 @@ |
||||
spring: |
||||
application: |
||||
name: jxc-gateway |
||||
|
||||
cloud: |
||||
nacos: |
||||
config: |
||||
server-addr: 127.0.0.1:8848 |
||||
shared-configs: |
||||
- data-id: jxc-common-database.yml |
||||
- data-id: jxc-common-redis.yml |
||||
file-extension: yml |
||||
|
||||
discovery: |
||||
server-addr: 127.0.0.1:8848 |
||||
@ -0,0 +1,25 @@ |
||||
spring: |
||||
datasource: |
||||
driver-class-name: com.mysql.cj.jdbc.Driver |
||||
url: jdbc:mysql://localhost:3306/repo?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true |
||||
username: username |
||||
password: password |
||||
|
||||
mybatis-plus: |
||||
mapper-locations: |
||||
- classpath*:/mapper/**/*.xml |
||||
configuration: |
||||
map-underscore-to-camel-case: false |
||||
local-cache-scope: STATEMENT |
||||
cache-enabled: false |
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl |
||||
global-config: |
||||
banner: false |
||||
db-config: |
||||
id-type: NONE |
||||
|
||||
pagehelper: |
||||
helperDialect: mysql |
||||
reasonable: true |
||||
supportMethodsArguments: true |
||||
params: count=countSql |
||||
@ -0,0 +1,8 @@ |
||||
qiniu: |
||||
access-key: |
||||
secret-key: |
||||
bucket: |
||||
#七牛的临时上传凭证的有效期,单位秒 |
||||
token-expires: 3600 |
||||
#七牛的临时上传凭证的redis键名 |
||||
redis-cache-key: qiniu-token |
||||
@ -0,0 +1,11 @@ |
||||
spring: |
||||
redis: |
||||
host: localhost |
||||
port: 6379 |
||||
timeout: 3000 |
||||
lettuce: |
||||
pool: |
||||
max-idle: 500 |
||||
min-idle: 50 |
||||
max-active: 2000 |
||||
max-wait: 1000 |
||||
@ -0,0 +1,60 @@ |
||||
server: |
||||
port: 8081 |
||||
|
||||
spring: |
||||
cloud: |
||||
gateway: |
||||
discovery: |
||||
locator: |
||||
enabled: true |
||||
lowerCaseServiceId: true |
||||
|
||||
default-filters: |
||||
- name: RequestRateLimiter |
||||
args: |
||||
redis-rate-limiter.replenishRate: 10 # 每秒处理多少个请求 |
||||
redis-rate-limiter.burstCapacity: 20 # 一秒钟内完成的最大请求数 |
||||
key-resolver: #{@urlKeyResolver} |
||||
|
||||
routes: |
||||
- id: jxc-web-account |
||||
uri: lb://jxc-web-account |
||||
predicates: |
||||
- Path=/account/** |
||||
|
||||
- id: jxc-web-document |
||||
uri: lb://jxc-web-document |
||||
predicates: |
||||
- Path=/document/** |
||||
|
||||
- id: jxc-web-message |
||||
uri: lb://jxc-web-message |
||||
predicates: |
||||
- Path=/message/** |
||||
|
||||
- id: jxc-web-record |
||||
uri: lb://jxc-web-record |
||||
predicates: |
||||
- Path=/record/** |
||||
|
||||
- id: jxc-web-statistic |
||||
uri: lb://jxc-web-statistic |
||||
predicates: |
||||
- Path=/statistic/** |
||||
|
||||
- id: jxc-web-stock |
||||
uri: lb://jxc-web-stock |
||||
predicates: |
||||
- Path=/stock/** |
||||
|
||||
- id: jxc-web-system |
||||
uri: lb://jxc-web-system |
||||
predicates: |
||||
- Path=/system/** |
||||
|
||||
whitelist: |
||||
- /test/** |
||||
- /account/login |
||||
- /account/logout |
||||
- /account/register |
||||
- /account/checkName |
||||
@ -0,0 +1,2 @@ |
||||
server: |
||||
port: 8000 |
||||
@ -0,0 +1,4 @@ |
||||
server: |
||||
port: 8001 |
||||
servlet: |
||||
context-path: /document |
||||
@ -0,0 +1,4 @@ |
||||
server: |
||||
port: 8002 |
||||
servlet: |
||||
context-path: /message |
||||
@ -0,0 +1,2 @@ |
||||
server: |
||||
port: 8003 |
||||
@ -0,0 +1,2 @@ |
||||
server: |
||||
port: 8004 |
||||
@ -0,0 +1,2 @@ |
||||
server: |
||||
port: 8005 |
||||
@ -0,0 +1,4 @@ |
||||
server: |
||||
port: 8006 |
||||
servlet: |
||||
context-path: /system |
||||
@ -0,0 +1,6 @@ |
||||
socket: |
||||
#hostname: 172.24.52.184 |
||||
hostname: localhost |
||||
port: 12580 |
||||
max-frame-payload-length: 1048576 |
||||
max-http-content-length: 1048576 |
||||
@ -0,0 +1,158 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<parent> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-parent</artifactId> |
||||
<version>2.2.7.RELEASE</version> |
||||
<relativePath/> <!-- lookup parent from repository --> |
||||
</parent> |
||||
<packaging>pom</packaging> |
||||
<modules> |
||||
<module>common</module> |
||||
<module>gateway</module> |
||||
<module>web</module> |
||||
<module>websocket</module> |
||||
<module>schedule</module> |
||||
<module>api</module> |
||||
</modules> |
||||
|
||||
<groupId>cn.toesbieya</groupId> |
||||
<artifactId>jxc</artifactId> |
||||
<version>0.0.1-SNAPSHOT</version> |
||||
<name>jxc</name> |
||||
|
||||
<properties> |
||||
<java.version>1.8</java.version> |
||||
|
||||
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version> |
||||
<spring-cloud.version>Hoxton.SR3</spring-cloud.version> |
||||
|
||||
<mybatis-plus.version>3.3.2</mybatis-plus.version> |
||||
<pagehelper.version>1.2.12</pagehelper.version> |
||||
<socketio.version>1.7.11</socketio.version> |
||||
<guava.version>28.1-jre</guava.version> |
||||
<fastjson.version>1.2.60</fastjson.version> |
||||
|
||||
<!--七牛云--> |
||||
<qiniu.version>[7.2.0, 7.2.99]</qiniu.version> |
||||
|
||||
<!--三方工具--> |
||||
<easyexcel.version>2.2.5</easyexcel.version> |
||||
</properties> |
||||
|
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>com.alibaba.cloud</groupId> |
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.alibaba.cloud</groupId> |
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.projectlombok</groupId> |
||||
<artifactId>lombok</artifactId> |
||||
</dependency> |
||||
</dependencies> |
||||
|
||||
<dependencyManagement> |
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>cn.toesbieya</groupId> |
||||
<artifactId>common</artifactId> |
||||
<version>${project.version}</version> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>cn.toesbieya</groupId> |
||||
<artifactId>web-common</artifactId> |
||||
<version>${project.version}</version> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>cn.toesbieya</groupId> |
||||
<artifactId>api</artifactId> |
||||
<version>${project.version}</version> |
||||
</dependency> |
||||
|
||||
<!--<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-dependencies</artifactId> |
||||
<version>${spring-boot.version}</version> |
||||
<type>pom</type> |
||||
<scope>import</scope> |
||||
</dependency>--> |
||||
|
||||
<dependency> |
||||
<groupId>org.springframework.cloud</groupId> |
||||
<artifactId>spring-cloud-dependencies</artifactId> |
||||
<version>${spring-cloud.version}</version> |
||||
<type>pom</type> |
||||
<scope>import</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.alibaba.cloud</groupId> |
||||
<artifactId>spring-cloud-alibaba-dependencies</artifactId> |
||||
<version>${spring-cloud-alibaba.version}</version> |
||||
<type>pom</type> |
||||
<scope>import</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.baomidou</groupId> |
||||
<artifactId>mybatis-plus-boot-starter</artifactId> |
||||
<version>${mybatis-plus.version}</version> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.github.pagehelper</groupId> |
||||
<artifactId>pagehelper-spring-boot-starter</artifactId> |
||||
<version>${pagehelper.version}</version> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.alibaba</groupId> |
||||
<artifactId>easyexcel</artifactId> |
||||
<version>${easyexcel.version}</version> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.qiniu</groupId> |
||||
<artifactId>qiniu-java-sdk</artifactId> |
||||
<version>${qiniu.version}</version> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.corundumstudio.socketio</groupId> |
||||
<artifactId>netty-socketio</artifactId> |
||||
<version>${socketio.version}</version> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.google.guava</groupId> |
||||
<artifactId>guava</artifactId> |
||||
<version>${guava.version}</version> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.alibaba</groupId> |
||||
<artifactId>fastjson</artifactId> |
||||
<version>${fastjson.version}</version> |
||||
</dependency> |
||||
</dependencies> |
||||
</dependencyManagement> |
||||
|
||||
<build> |
||||
<plugins> |
||||
<plugin> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-maven-plugin</artifactId> |
||||
</plugin> |
||||
</plugins> |
||||
</build> |
||||
</project> |
||||
@ -0,0 +1,35 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<parent> |
||||
<artifactId>jxc</artifactId> |
||||
<groupId>cn.toesbieya</groupId> |
||||
<version>0.0.1-SNAPSHOT</version> |
||||
</parent> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
|
||||
<artifactId>shedule</artifactId> |
||||
|
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>cn.toesbieya</groupId> |
||||
<artifactId>common</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>mysql</groupId> |
||||
<artifactId>mysql-connector-java</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.baomidou</groupId> |
||||
<artifactId>mybatis-plus-boot-starter</artifactId> |
||||
</dependency> |
||||
</dependencies> |
||||
</project> |
||||
@ -0,0 +1,16 @@ |
||||
package cn.toesbieya.jxc.schedule; |
||||
|
||||
import org.mybatis.spring.annotation.MapperScan; |
||||
import org.springframework.boot.WebApplicationType; |
||||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||
import org.springframework.boot.builder.SpringApplicationBuilder; |
||||
|
||||
@SpringBootApplication(scanBasePackages = "cn.toesbieya.jxc") |
||||
@MapperScan("cn.toesbieya.jxc.schedule.mapper") |
||||
public class ScheduleApplication { |
||||
public static void main(String[] args) { |
||||
new SpringApplicationBuilder(ScheduleApplication.class) |
||||
.web(WebApplicationType.NONE) |
||||
.run(args); |
||||
} |
||||
} |
||||
@ -0,0 +1,45 @@ |
||||
package cn.toesbieya.jxc.schedule.config; |
||||
|
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.scheduling.annotation.AsyncConfigurer; |
||||
import org.springframework.scheduling.annotation.EnableAsync; |
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
||||
|
||||
import java.util.concurrent.Executor; |
||||
import java.util.concurrent.ThreadPoolExecutor; |
||||
|
||||
@Configuration |
||||
@EnableAsync |
||||
@Slf4j |
||||
public class ExecutorConfig implements AsyncConfigurer { |
||||
|
||||
@Override |
||||
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { |
||||
return (ex, method, params) -> { |
||||
log.error("异步线程执行失败。方法:[{}],异常信息[{}] : ", method, ex.getMessage(), ex); |
||||
}; |
||||
} |
||||
|
||||
//定时任务线程池
|
||||
@Bean |
||||
public Executor scheduledExecutor() { |
||||
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); |
||||
//核心线程数
|
||||
threadPoolTaskExecutor.setCorePoolSize(3); |
||||
//是否回收空闲线程
|
||||
threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true); |
||||
//最大线程数
|
||||
threadPoolTaskExecutor.setMaxPoolSize(5); |
||||
//配置等待队列大小
|
||||
threadPoolTaskExecutor.setQueueCapacity(2); |
||||
//配置线程池前缀
|
||||
threadPoolTaskExecutor.setThreadNamePrefix("scheduled-"); |
||||
//拒绝策略
|
||||
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy()); |
||||
threadPoolTaskExecutor.initialize(); |
||||
return threadPoolTaskExecutor; |
||||
} |
||||
} |
||||
@ -0,0 +1,33 @@ |
||||
package cn.toesbieya.jxc.schedule.mapper; |
||||
|
||||
import cn.toesbieya.jxc.common.model.entity.StatFinishOrder; |
||||
import cn.toesbieya.jxc.common.model.entity.StatProfitGoods; |
||||
import cn.toesbieya.jxc.common.model.entity.StatProfitTotal; |
||||
import org.apache.ibatis.annotations.Param; |
||||
|
||||
import java.math.BigDecimal; |
||||
import java.util.List; |
||||
|
||||
public interface StatisticMapper { |
||||
boolean checkDailyFinishOrderExist(@Param("time") long time); |
||||
|
||||
boolean checkDailyProfitExist(@Param("time") long time); |
||||
|
||||
int insertFinishOrder(StatFinishOrder param); |
||||
|
||||
int insertProfitTotal(StatProfitTotal param); |
||||
|
||||
int insertProfitGoodsBatch(@Param("list") List<StatProfitGoods> list); |
||||
|
||||
BigDecimal getPurchaseOrderDailyTotalPurchasePrice(@Param("start") Long start, @Param("end") Long end); |
||||
|
||||
List<StatProfitGoods> getPurchaseOrderDailyProfitGoods(@Param("start") long start, @Param("end") long end); |
||||
|
||||
Integer getPurchaseOrderLastDayFinishOrderNum(); |
||||
|
||||
BigDecimal getSellOrderDailyTotalPurchasePrice(@Param("start") Long start, @Param("end") Long end); |
||||
|
||||
List<StatProfitGoods> getSellOrderDailyProfitGoods(@Param("start") long start, @Param("end") long end); |
||||
|
||||
Integer getSellOrderLastDayFinishOrderNum(); |
||||
} |
||||
@ -0,0 +1,106 @@ |
||||
package cn.toesbieya.jxc.schedule.task; |
||||
|
||||
import cn.toesbieya.jxc.common.constant.DocumentConstant; |
||||
import cn.toesbieya.jxc.common.utils.DateUtil; |
||||
import cn.toesbieya.jxc.common.utils.RedisUtil; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.boot.CommandLineRunner; |
||||
import org.springframework.context.annotation.DependsOn; |
||||
import org.springframework.scheduling.annotation.Async; |
||||
import org.springframework.scheduling.annotation.Scheduled; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import java.time.Instant; |
||||
import java.time.temporal.ChronoUnit; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
@Component |
||||
@Slf4j |
||||
@DependsOn("redisUtil") |
||||
public class DocumentTask implements CommandLineRunner { |
||||
@Override |
||||
public void run(String... args) { |
||||
template("初始化单据ID", 0); |
||||
} |
||||
|
||||
//每天零点重置单据ID的值
|
||||
@Async("scheduledExecutor") |
||||
@Scheduled(cron = "1 0 0 */1 * ?") |
||||
public void autoRefreshDocumentID() { |
||||
template("定时更新单据ID", 1); |
||||
} |
||||
|
||||
//启动时初始化单据ID
|
||||
private void initDocumentsID() { |
||||
String[] fields = new String[DocumentConstant.DOCUMENT_TYPE.length + 1]; |
||||
fields[0] = "date"; |
||||
System.arraycopy(DocumentConstant.DOCUMENT_TYPE, 0, fields, 1, fields.length - 1); |
||||
|
||||
List<Object> result = RedisUtil.hmget(DocumentConstant.DOCUMENT_TYPE_REDIS_KEY, fields); |
||||
|
||||
long now = DateUtil.getTimestampNow(); |
||||
|
||||
//日期不是今天时,更新全部
|
||||
Object firstItem = result.get(0); |
||||
if (null == firstItem || now != (long) firstItem) { |
||||
updateAllDocument(now); |
||||
log.info("更新【{}】种单据ID成功", DocumentConstant.DOCUMENT_TYPE.length); |
||||
return; |
||||
} |
||||
|
||||
//判断是否有新增的单据类型,有则更新
|
||||
int updateNum = 0; |
||||
Map<String, Object> update = new HashMap<>(); |
||||
int resultSize = result.size(); |
||||
|
||||
for (int i = 1; i < resultSize; i++) { |
||||
if (result.get(i) == null) { |
||||
updateNum++; |
||||
update.put(DocumentConstant.DOCUMENT_TYPE[i - 1], 1); |
||||
} |
||||
} |
||||
|
||||
if (updateNum > 0) { |
||||
RedisUtil.hmset(DocumentConstant.DOCUMENT_TYPE_REDIS_KEY, update); |
||||
log.info("更新增加的单据【{}】种", updateNum); |
||||
} |
||||
} |
||||
|
||||
//每种单据的值置为1
|
||||
private void updateAllDocument(long now) { |
||||
Map<String, Object> map = new HashMap<>(); |
||||
map.put("date", now); |
||||
for (String v : DocumentConstant.DOCUMENT_TYPE) { |
||||
map.put(v, 1); |
||||
} |
||||
RedisUtil.hmset(DocumentConstant.DOCUMENT_TYPE_REDIS_KEY, map); |
||||
} |
||||
|
||||
//模板
|
||||
private void template(String action, int type) { |
||||
Instant start = Instant.now(); |
||||
|
||||
String lockKey = DocumentConstant.UPDATE_DOCUMENTS_LOCK_KEY; |
||||
|
||||
try (RedisUtil.Locker locker = new RedisUtil.Locker(lockKey)) { |
||||
if (!locker.lock()) { |
||||
log.error("{}失败,获取锁【{}】失败", action, lockKey); |
||||
return; |
||||
} |
||||
|
||||
if (type == 0) { |
||||
initDocumentsID(); |
||||
} |
||||
else { |
||||
updateAllDocument(DateUtil.getTimestampNow()); |
||||
} |
||||
|
||||
log.info("{}成功,耗时:{}毫秒", action, ChronoUnit.MILLIS.between(start, Instant.now())); |
||||
} |
||||
catch (Exception e) { |
||||
log.error("{}失败", action, e); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,66 @@ |
||||
package cn.toesbieya.jxc.schedule.task; |
||||
|
||||
import cn.toesbieya.jxc.common.constant.SessionConstant; |
||||
import cn.toesbieya.jxc.common.constant.SocketConstant; |
||||
import cn.toesbieya.jxc.common.model.vo.SocketOfflineVo; |
||||
import cn.toesbieya.jxc.common.utils.RedisUtil; |
||||
import cn.toesbieya.jxc.common.utils.SessionUtil; |
||||
import com.alibaba.fastjson.JSONObject; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.boot.CommandLineRunner; |
||||
import org.springframework.context.annotation.DependsOn; |
||||
import org.springframework.scheduling.annotation.Async; |
||||
import org.springframework.scheduling.annotation.Scheduled; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
|
||||
@Component |
||||
@Slf4j |
||||
@DependsOn("redisUtil") |
||||
public class SessionTask implements CommandLineRunner { |
||||
@Override |
||||
public void run(String... args) { |
||||
cleanSession(); |
||||
} |
||||
|
||||
@Async("scheduledExecutor") |
||||
@Scheduled(cron = "0 */10 * * * ?") |
||||
public void autoCleanSession() { |
||||
cleanSession(); |
||||
} |
||||
|
||||
//清理session中未活动的用户
|
||||
private void cleanSession() { |
||||
try { |
||||
long now = System.currentTimeMillis(); |
||||
|
||||
//获取所有用户session
|
||||
Set<String> sessionKeys = RedisUtil.keys(SessionConstant.REDIS_NAMESPACE + "*"); |
||||
|
||||
//获取离线情况 (k:sessionKey,v:SocketOfflineVo)
|
||||
Map<Object, Object> offlineMap = RedisUtil.hgetAll(SocketConstant.REDIS_OFFLINE_USER); |
||||
|
||||
//移除无活动时间超出限定的用户
|
||||
sessionKeys.forEach(sessionKey -> { |
||||
Object obj = offlineMap.get(sessionKey); |
||||
if (obj != null) { |
||||
SocketOfflineVo offlineVo = ((JSONObject) obj).toJavaObject(SocketOfflineVo.class); |
||||
if (now > offlineVo.getTime() + SessionConstant.NO_ACTION_INTERVAL) { |
||||
//删除session
|
||||
RedisUtil.del(sessionKey); |
||||
|
||||
//删除离线表信息
|
||||
String token = sessionKey.replace(SessionConstant.REDIS_NAMESPACE, ""); |
||||
Integer uid = SessionUtil.getUidFromToken(token); |
||||
RedisUtil.hdel(SocketConstant.REDIS_OFFLINE_USER, String.valueOf(uid)); |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
catch (Exception e) { |
||||
log.error("清理session失败", e); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,116 @@ |
||||
package cn.toesbieya.jxc.schedule.task; |
||||
|
||||
import cn.toesbieya.jxc.common.model.entity.StatFinishOrder; |
||||
import cn.toesbieya.jxc.common.model.entity.StatProfitGoods; |
||||
import cn.toesbieya.jxc.common.model.entity.StatProfitTotal; |
||||
import cn.toesbieya.jxc.common.utils.DateUtil; |
||||
import cn.toesbieya.jxc.schedule.mapper.StatisticMapper; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.boot.CommandLineRunner; |
||||
import org.springframework.scheduling.annotation.Async; |
||||
import org.springframework.scheduling.annotation.Scheduled; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import javax.annotation.Resource; |
||||
import java.math.BigDecimal; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
|
||||
@Component |
||||
@Slf4j |
||||
public class StatisticTask implements CommandLineRunner { |
||||
@Resource |
||||
private StatisticMapper statisticMapper; |
||||
|
||||
@Override |
||||
public void run(String... args) throws Exception { |
||||
this.autoStat(); |
||||
} |
||||
|
||||
//每天零点统计信息
|
||||
@Async("scheduledExecutor") |
||||
@Scheduled(cron = "1 0 0 */1 * ?") |
||||
public void autoStat() { |
||||
log.info("开始统计信息..."); |
||||
|
||||
long lastDay = DateUtil.getTimestampBeforeNow(1); |
||||
long today = DateUtil.getTimestampNow(); |
||||
|
||||
//检查是否已统计过昨日已完成的订单数
|
||||
if (!statisticMapper.checkDailyFinishOrderExist(lastDay)) { |
||||
Integer lastDayPurchaseOrderFinish = statisticMapper.getPurchaseOrderLastDayFinishOrderNum(); |
||||
Integer lastDaySellOrderFinish = statisticMapper.getSellOrderLastDayFinishOrderNum(); |
||||
|
||||
StatFinishOrder param = new StatFinishOrder(); |
||||
param.setPurchase(lastDayPurchaseOrderFinish); |
||||
param.setSell(lastDaySellOrderFinish); |
||||
param.setTime(lastDay); |
||||
|
||||
statisticMapper.insertFinishOrder(param); |
||||
} |
||||
|
||||
//检查是否已统计过昨日的采购额、销售额、毛利
|
||||
if (!statisticMapper.checkDailyProfitExist(lastDay)) { |
||||
//昨日各个商品的采购额、销售额
|
||||
List<StatProfitGoods> purchaseProfitGoods = statisticMapper.getPurchaseOrderDailyProfitGoods(lastDay, today); |
||||
List<StatProfitGoods> sellProfitGoods = statisticMapper.getSellOrderDailyProfitGoods(lastDay, today); |
||||
|
||||
//昨日的采购总额、销售总额
|
||||
BigDecimal totalPurchase = new BigDecimal(0); |
||||
BigDecimal totalSell = new BigDecimal(0); |
||||
|
||||
//根据商品分类计算
|
||||
Map<Integer, StatProfitGoods> map = new HashMap<>(sellProfitGoods.size()); |
||||
|
||||
for (StatProfitGoods p : sellProfitGoods) { |
||||
map.put(p.getCid(), p); |
||||
} |
||||
|
||||
for (StatProfitGoods p : purchaseProfitGoods) { |
||||
p.setTime(lastDay); |
||||
Integer cid = p.getCid(); |
||||
BigDecimal purchase = p.getPurchase(); |
||||
totalPurchase = totalPurchase.add(purchase); |
||||
StatProfitGoods s = map.get(cid); |
||||
|
||||
if (s != null) { |
||||
BigDecimal sell = s.getSell(); |
||||
totalSell = totalSell.add(sell); |
||||
p.setSell(sell); |
||||
p.setProfit(sell.subtract(purchase)); |
||||
map.remove(cid); |
||||
} |
||||
else { |
||||
p.setSell(new BigDecimal(0)); |
||||
p.setProfit(p.getSell().subtract(purchase)); |
||||
} |
||||
} |
||||
|
||||
for (StatProfitGoods s : map.values()) { |
||||
s.setTime(lastDay); |
||||
s.setPurchase(new BigDecimal(0)); |
||||
BigDecimal sell = s.getSell(); |
||||
s.setProfit(sell); |
||||
totalSell = totalSell.add(sell); |
||||
purchaseProfitGoods.add(s); |
||||
} |
||||
|
||||
BigDecimal totalProfit = totalSell.subtract(totalPurchase); |
||||
|
||||
StatProfitTotal statProfitTotal = new StatProfitTotal(); |
||||
statProfitTotal.setPurchase(totalPurchase); |
||||
statProfitTotal.setSell(totalSell); |
||||
statProfitTotal.setProfit(totalProfit); |
||||
statProfitTotal.setTime(lastDay); |
||||
|
||||
statisticMapper.insertProfitTotal(statProfitTotal); |
||||
|
||||
if (purchaseProfitGoods.size() > 0) { |
||||
statisticMapper.insertProfitGoodsBatch(purchaseProfitGoods); |
||||
} |
||||
} |
||||
|
||||
log.info("结束统计信息..."); |
||||
} |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
spring: |
||||
application: |
||||
name: jxc-schedule |
||||
|
||||
cloud: |
||||
nacos: |
||||
config: |
||||
server-addr: 127.0.0.1:8848 |
||||
file-extension: yml |
||||
shared-configs: |
||||
- data-id: jxc-common-database.yml |
||||
- data-id: jxc-common-redis.yml |
||||
discovery: |
||||
server-addr: 127.0.0.1:8848 |
||||
@ -0,0 +1,100 @@ |
||||
<?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="cn.toesbieya.jxc.schedule.mapper.StatisticMapper"> |
||||
<select id="checkDailyFinishOrderExist" resultType="java.lang.Boolean"> |
||||
select count(0) |
||||
from stat_finish_order |
||||
where time = #{time} |
||||
</select> |
||||
|
||||
<select id="checkDailyProfitExist" resultType="java.lang.Boolean"> |
||||
select count(0) |
||||
from stat_profit_total |
||||
where time = #{time} |
||||
</select> |
||||
|
||||
<insert id="insertFinishOrder" parameterType="cn.toesbieya.jxc.common.model.entity.StatFinishOrder"> |
||||
insert into stat_finish_order (purchase, sell, time) |
||||
values (#{purchase}, #{sell}, #{time}) |
||||
</insert> |
||||
|
||||
<insert id="insertProfitTotal" parameterType="cn.toesbieya.jxc.common.model.entity.StatProfitTotal"> |
||||
insert into stat_profit_total (purchase, sell, profit, time) |
||||
values (#{purchase}, #{sell}, #{profit}, #{time}) |
||||
</insert> |
||||
|
||||
<insert id="insertProfitGoodsBatch" keyColumn="id" keyProperty="id" |
||||
parameterType="cn.toesbieya.jxc.common.model.entity.StatProfitGoods" |
||||
useGeneratedKeys="true"> |
||||
insert into stat_profit_goods |
||||
(cid, purchase, sell, profit, time) |
||||
values |
||||
<foreach collection="list" item="item" separator=","> |
||||
(#{item.cid}, #{item.purchase}, #{item.sell}, #{item.profit}, #{item.time}) |
||||
</foreach> |
||||
</insert> |
||||
|
||||
<select id="getPurchaseOrderDailyTotalPurchasePrice" resultType="java.math.BigDecimal"> |
||||
select ifnull(sum(total), 0) total |
||||
from biz_purchase_order |
||||
where status = 2 |
||||
<if test="start != null"> |
||||
and vtime <![CDATA[ >= ]]> #{start} |
||||
</if> |
||||
<if test="end != null"> |
||||
and vtime <![CDATA[ <= ]]> #{end} |
||||
</if> |
||||
</select> |
||||
|
||||
<select id="getPurchaseOrderLastDayFinishOrderNum" resultType="java.lang.Integer"> |
||||
select count(0) |
||||
from biz_purchase_order |
||||
where status = 2 |
||||
and finish = 2 |
||||
and ftime between unix_timestamp(cast(sysdate() as date) - interval 1 day) * 1000 and unix_timestamp(cast(sysdate() as date)) * 1000 |
||||
</select> |
||||
|
||||
<select id="getPurchaseOrderDailyProfitGoods" resultType="cn.toesbieya.jxc.common.model.entity.StatProfitGoods"> |
||||
select t1.*, |
||||
t2.name cname |
||||
from (select a.cid, |
||||
ifnull(sum(a.num * a.price), 0) purchase |
||||
from biz_purchase_order_sub a |
||||
join biz_purchase_order b on a.pid = b.id |
||||
where b.vtime between #{start} and #{end} |
||||
group by a.cid) t1 |
||||
join sys_category t2 on t1.cid = t2.id |
||||
</select> |
||||
|
||||
<select id="getSellOrderDailyTotalPurchasePrice" resultType="java.math.BigDecimal"> |
||||
select ifnull(sum(total), 0) total |
||||
from biz_sell_order |
||||
where status = 2 |
||||
<if test="start != null"> |
||||
and vtime <![CDATA[ >= ]]> #{start} |
||||
</if> |
||||
<if test="end != null"> |
||||
and vtime <![CDATA[ <= ]]> #{end} |
||||
</if> |
||||
</select> |
||||
|
||||
<select id="getSellOrderLastDayFinishOrderNum" resultType="java.lang.Integer"> |
||||
select count(0) |
||||
from biz_sell_order |
||||
where status = 2 |
||||
and finish = 2 |
||||
and ftime between unix_timestamp(cast(sysdate() as date) - interval 1 day) * 1000 and unix_timestamp(cast(sysdate() as date)) * 1000 |
||||
</select> |
||||
|
||||
<select id="getSellOrderDailyProfitGoods" resultType="cn.toesbieya.jxc.common.model.entity.StatProfitGoods"> |
||||
select t1.*, |
||||
t2.name cname |
||||
from (select a.cid, |
||||
ifnull(sum(a.num * a.price), 0) sell |
||||
from biz_sell_order_sub a |
||||
join biz_sell_order b on a.pid = b.id |
||||
where b.vtime between #{start} and #{end} |
||||
group by a.cid) t1 |
||||
join sys_category t2 on t1.cid = t2.id |
||||
</select> |
||||
</mapper> |
||||
@ -0,0 +1,51 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<parent> |
||||
<artifactId>jxc</artifactId> |
||||
<groupId>cn.toesbieya</groupId> |
||||
<version>0.0.1-SNAPSHOT</version> |
||||
</parent> |
||||
<packaging>pom</packaging> |
||||
<modules> |
||||
<module>web-modules</module> |
||||
<module>web-common</module> |
||||
</modules> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
|
||||
<artifactId>web</artifactId> |
||||
|
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>cn.toesbieya</groupId> |
||||
<artifactId>common</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>cn.toesbieya</groupId> |
||||
<artifactId>api</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-web</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.alibaba.cloud</groupId> |
||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> |
||||
<exclusions> |
||||
<exclusion> |
||||
<artifactId>jackson-dataformat-xml</artifactId> |
||||
<groupId>com.fasterxml.jackson.dataformat</groupId> |
||||
</exclusion> |
||||
</exclusions> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.alibaba.cloud</groupId> |
||||
<artifactId>spring-cloud-starter-dubbo</artifactId> |
||||
</dependency> |
||||
</dependencies> |
||||
</project> |
||||
@ -0,0 +1,45 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<parent> |
||||
<artifactId>web</artifactId> |
||||
<groupId>cn.toesbieya</groupId> |
||||
<version>0.0.1-SNAPSHOT</version> |
||||
</parent> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
|
||||
<artifactId>web-common</artifactId> |
||||
|
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-aop</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.alibaba</groupId> |
||||
<artifactId>easyexcel</artifactId> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.qiniu</groupId> |
||||
<artifactId>qiniu-java-sdk</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>com.squareup.okhttp3</groupId> |
||||
<artifactId>okhttp</artifactId> |
||||
<scope>compile</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>com.google.code.gson</groupId> |
||||
<artifactId>gson</artifactId> |
||||
<scope>compile</scope> |
||||
</dependency> |
||||
|
||||
<dependency> |
||||
<groupId>com.github.pagehelper</groupId> |
||||
<artifactId>pagehelper-spring-boot-starter</artifactId> |
||||
</dependency> |
||||
</dependencies> |
||||
</project> |
||||
@ -0,0 +1,10 @@ |
||||
package cn.toesbieya.jxc.web.common.annoation; |
||||
|
||||
import java.lang.annotation.*; |
||||
|
||||
@Target(ElementType.METHOD) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
public @interface Lock { |
||||
String[] value() default {}; |
||||
} |
||||
@ -0,0 +1,10 @@ |
||||
package cn.toesbieya.jxc.web.common.annoation; |
||||
|
||||
import java.lang.annotation.*; |
||||
|
||||
@Target(ElementType.METHOD) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
public @interface TimeCost { |
||||
|
||||
} |
||||
@ -0,0 +1,10 @@ |
||||
package cn.toesbieya.jxc.web.common.annoation; |
||||
|
||||
import java.lang.annotation.*; |
||||
|
||||
@Target(ElementType.METHOD) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
public @interface UserAction { |
||||
String value() default ""; |
||||
} |
||||
@ -0,0 +1,82 @@ |
||||
package cn.toesbieya.jxc.web.common.aspect; |
||||
|
||||
import cn.toesbieya.jxc.common.model.vo.Result; |
||||
import cn.toesbieya.jxc.common.utils.RedisUtil; |
||||
import cn.toesbieya.jxc.common.utils.SpringUtil; |
||||
import cn.toesbieya.jxc.web.common.annoation.Lock; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.aspectj.lang.ProceedingJoinPoint; |
||||
import org.aspectj.lang.annotation.*; |
||||
import org.aspectj.lang.reflect.MethodSignature; |
||||
import org.springframework.core.annotation.Order; |
||||
import org.springframework.stereotype.Component; |
||||
import org.springframework.util.CollectionUtils; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.util.ArrayList; |
||||
|
||||
@Component |
||||
@Aspect |
||||
@Slf4j |
||||
@Order(Integer.MAX_VALUE - 1) |
||||
public class LockAspect { |
||||
private final ThreadLocal<ArrayList<RedisUtil.Locker>> lockHelperThreadLocal = new ThreadLocal<>(); |
||||
|
||||
@Pointcut("@annotation(cn.toesbieya.jxc.web.common.annoation.Lock)&&execution(cn.toesbieya.jxc.common.model.vo.Result cn.toesbieya.jxc..*.*(..))") |
||||
public void pointCut() { |
||||
|
||||
} |
||||
|
||||
@Around("pointCut()") |
||||
public Object around(ProceedingJoinPoint point) throws Throwable { |
||||
MethodSignature signature = (MethodSignature) point.getSignature(); |
||||
Method method = signature.getMethod(); |
||||
String[] values = method.getAnnotation(Lock.class).value(); |
||||
|
||||
if (values.length <= 0) { |
||||
return point.proceed(); |
||||
} |
||||
|
||||
ArrayList<RedisUtil.Locker> locks = new ArrayList<>(); |
||||
String[] parameterNames = signature.getParameterNames(); |
||||
Object[] args = point.getArgs(); |
||||
|
||||
for (String v : values) { |
||||
String lockKey = (String) SpringUtil.spell(v, parameterNames, args); |
||||
|
||||
//跳过空值
|
||||
if (StringUtils.isEmpty(lockKey)) continue; |
||||
|
||||
RedisUtil.Locker lockHelper = new RedisUtil.Locker(lockKey); |
||||
|
||||
if (!lockHelper.lock()) { |
||||
locks.forEach(RedisUtil.Locker::close); |
||||
return Result.fail("操作失败,请刷新后重试"); |
||||
} |
||||
|
||||
locks.add(lockHelper); |
||||
} |
||||
|
||||
lockHelperThreadLocal.set(locks); |
||||
return point.proceed(); |
||||
} |
||||
|
||||
@AfterReturning("pointCut()") |
||||
public void afterReturning() { |
||||
unlock(); |
||||
} |
||||
|
||||
@AfterThrowing("pointCut()") |
||||
public void afterThrowing() { |
||||
unlock(); |
||||
} |
||||
|
||||
private void unlock() { |
||||
ArrayList<RedisUtil.Locker> locks = lockHelperThreadLocal.get(); |
||||
if (!CollectionUtils.isEmpty(locks)) { |
||||
locks.forEach(RedisUtil.Locker::close); |
||||
lockHelperThreadLocal.remove(); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,80 @@ |
||||
package cn.toesbieya.jxc.web.common.aspect; |
||||
|
||||
import cn.toesbieya.jxc.api.service.RecordApi; |
||||
import cn.toesbieya.jxc.common.exception.JsonResultException; |
||||
import cn.toesbieya.jxc.common.model.entity.RecUserAction; |
||||
import cn.toesbieya.jxc.common.model.vo.Result; |
||||
import cn.toesbieya.jxc.common.utils.Util; |
||||
import cn.toesbieya.jxc.web.common.enumeration.UserActionEnum; |
||||
import cn.toesbieya.jxc.web.common.utils.ThreadUtil; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.dubbo.config.annotation.Reference; |
||||
import org.springframework.http.converter.HttpMessageNotReadableException; |
||||
import org.springframework.util.StringUtils; |
||||
import org.springframework.web.HttpRequestMethodNotSupportedException; |
||||
import org.springframework.web.bind.MissingServletRequestParameterException; |
||||
import org.springframework.web.bind.annotation.ExceptionHandler; |
||||
import org.springframework.web.bind.annotation.RestControllerAdvice; |
||||
import org.springframework.web.multipart.MaxUploadSizeExceededException; |
||||
|
||||
@Slf4j |
||||
@RestControllerAdvice |
||||
public class RestExceptionAspect { |
||||
@Reference |
||||
private RecordApi recordApi; |
||||
|
||||
//抛出异常时希望返回给前台
|
||||
@ExceptionHandler(JsonResultException.class) |
||||
public Result handle(JsonResultException e) { |
||||
recordUserAction(e); |
||||
return Result.fail(e.getMessage()); |
||||
} |
||||
|
||||
//请求方法有误
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class) |
||||
public Result handle(HttpRequestMethodNotSupportedException e) { |
||||
return Result.fail("请求方法有误"); |
||||
} |
||||
|
||||
//@RequestParam没有匹配到值
|
||||
@ExceptionHandler(MissingServletRequestParameterException.class) |
||||
public Result handle(MissingServletRequestParameterException e) { |
||||
log.error(e.getMessage(),e); |
||||
return Result.fail("get参数有误"); |
||||
} |
||||
|
||||
//@RequestBody没有匹配到值
|
||||
@ExceptionHandler(HttpMessageNotReadableException.class) |
||||
public Result handle(HttpMessageNotReadableException e) { |
||||
log.error(e.getMessage(),e); |
||||
return Result.fail("post参数有误"); |
||||
} |
||||
|
||||
//上传文件过大
|
||||
@ExceptionHandler(MaxUploadSizeExceededException.class) |
||||
public Result handle(MaxUploadSizeExceededException e) { |
||||
log.error("上传的文件超出限定大小,{}", e.getMessage()); |
||||
recordUserAction(e); |
||||
return Result.fail("上传的文件过大"); |
||||
} |
||||
|
||||
//最终捕获
|
||||
@ExceptionHandler(Exception.class) |
||||
public Result handle(Exception e) { |
||||
log.error("服务运行异常", e); |
||||
recordUserAction(e); |
||||
return Result.fail("服务运行异常"); |
||||
} |
||||
|
||||
private void recordUserAction(Exception e) { |
||||
RecUserAction action = ThreadUtil.getAction(); |
||||
|
||||
if (action != null && !StringUtils.isEmpty(action.getAction())) { |
||||
action.setError(Util.exception2Str(e)); |
||||
action.setType(UserActionEnum.FAIL.getCode()); |
||||
recordApi.insertUserAction(action); |
||||
} |
||||
|
||||
ThreadUtil.clearAll(); |
||||
} |
||||
} |
||||
@ -0,0 +1,45 @@ |
||||
package cn.toesbieya.jxc.web.common.aspect; |
||||
|
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.aspectj.lang.JoinPoint; |
||||
import org.aspectj.lang.annotation.AfterReturning; |
||||
import org.aspectj.lang.annotation.Aspect; |
||||
import org.aspectj.lang.annotation.Before; |
||||
import org.aspectj.lang.annotation.Pointcut; |
||||
import org.aspectj.lang.reflect.MethodSignature; |
||||
import org.springframework.core.annotation.Order; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.time.Instant; |
||||
import java.time.temporal.ChronoUnit; |
||||
|
||||
@Component |
||||
@Aspect |
||||
@Slf4j |
||||
@Order(1) |
||||
public class TimeCostAspect { |
||||
private final ThreadLocal<Instant> instantThreadLocal = new ThreadLocal<>(); |
||||
|
||||
@Pointcut("@annotation(cn.toesbieya.jxc.web.common.annoation.TimeCost)") |
||||
public void pointCut() { |
||||
|
||||
} |
||||
|
||||
@Before("pointCut()") |
||||
public void before() { |
||||
instantThreadLocal.set(Instant.now()); |
||||
} |
||||
|
||||
@AfterReturning("pointCut()") |
||||
public void afterReturning(JoinPoint point) { |
||||
if (null != instantThreadLocal.get()) { |
||||
MethodSignature signature = (MethodSignature) point.getSignature(); |
||||
Method method = signature.getMethod(); |
||||
log.info("类:{},方法:{},耗时:{}毫秒", |
||||
method.getDeclaringClass().getName(), |
||||
method.getName(), |
||||
ChronoUnit.MILLIS.between(instantThreadLocal.get(), Instant.now())); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,74 @@ |
||||
package cn.toesbieya.jxc.web.common.aspect; |
||||
|
||||
import cn.toesbieya.jxc.api.service.RecordApi; |
||||
import cn.toesbieya.jxc.common.model.entity.RecUserAction; |
||||
import cn.toesbieya.jxc.common.model.vo.Result; |
||||
import cn.toesbieya.jxc.common.utils.SpringUtil; |
||||
import cn.toesbieya.jxc.web.common.annoation.UserAction; |
||||
import cn.toesbieya.jxc.web.common.enumeration.UserActionEnum; |
||||
import cn.toesbieya.jxc.web.common.utils.ThreadUtil; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.dubbo.config.annotation.Reference; |
||||
import org.aspectj.lang.ProceedingJoinPoint; |
||||
import org.aspectj.lang.annotation.Around; |
||||
import org.aspectj.lang.annotation.Aspect; |
||||
import org.aspectj.lang.annotation.Pointcut; |
||||
import org.aspectj.lang.reflect.MethodSignature; |
||||
import org.springframework.core.annotation.Order; |
||||
import org.springframework.stereotype.Component; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
import java.lang.reflect.Method; |
||||
|
||||
@Component |
||||
@Aspect |
||||
@Slf4j |
||||
@Order(Integer.MAX_VALUE - 2) |
||||
public class UserActionAspect { |
||||
@Reference |
||||
private RecordApi recordApi; |
||||
|
||||
@Pointcut("@annotation(cn.toesbieya.jxc.web.common.annoation.UserAction)&&execution(cn.toesbieya.jxc.common.model.vo.Result cn.toesbieya.jxc..*.*(..))") |
||||
public void pointCut() { |
||||
|
||||
} |
||||
|
||||
@Around("pointCut()") |
||||
public Object around(ProceedingJoinPoint pjp) throws Throwable { |
||||
RecUserAction action = ThreadUtil.getAction(); |
||||
|
||||
if (action == null) { |
||||
return pjp.proceed(); |
||||
} |
||||
|
||||
action.setTime(System.currentTimeMillis()); |
||||
|
||||
MethodSignature signature = (MethodSignature) pjp.getSignature(); |
||||
Method method = signature.getMethod(); |
||||
Object[] args = pjp.getArgs(); |
||||
String[] argNames = signature.getParameterNames(); |
||||
UserAction annotation = method.getAnnotation(UserAction.class); |
||||
|
||||
//优先使用注解表达式
|
||||
if (!StringUtils.isEmpty(annotation.value())) { |
||||
String str = (String) SpringUtil.spell(annotation.value(), argNames, args); |
||||
if (!StringUtils.isEmpty(str)) { |
||||
action.setAction(str); |
||||
} |
||||
} |
||||
|
||||
Result result = (Result) pjp.proceed(); |
||||
|
||||
if (result.isSuccess()) { |
||||
action.setType(UserActionEnum.SUCCESS.getCode()); |
||||
} |
||||
else { |
||||
action.setType(UserActionEnum.FAIL.getCode()); |
||||
action.setError(result.getMsg()); |
||||
} |
||||
|
||||
recordApi.insertUserAction(action); |
||||
|
||||
return result; |
||||
} |
||||
} |
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue