Commit 3ab6e756 authored by shengnan hu's avatar shengnan hu
Browse files

init

parents
Pipeline #294 passed with stage
in 2 minutes and 13 seconds
<?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>mall4cloud-common</artifactId>
<groupId>com.mall4j.cloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mall4cloud-common-database</artifactId>
<description>mall4cloud 数据库连接相关代码</description>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.mall4j.cloud</groupId>
<artifactId>mall4cloud-common-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>com.mall4j.cloud</groupId>
<artifactId>mall4cloud-api-leaf</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
package com.mall4j.cloud.common.database.annotations;
import com.mall4j.cloud.common.database.interceptor.GeneratedKeyInterceptor;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 分布式id标识
* 如果一个类是model中的类,也就是继承BaseModel的类,并且字段含有该注解,当插入数据的时候,会往该字段插入分布式id
* @author FrozenWatermelon
* @date 2020/09/09
* @see GeneratedKeyInterceptor
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributedId {
String value();
}
package com.mall4j.cloud.common.database.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
/**
* @author FrozenWatermelon
* @date 2020/6/24
*/
@Configuration
@MapperScan({ "com.mall4j.cloud.**.mapper" })
public class MybatisConfig {
}
package com.mall4j.cloud.common.database.config;
import cn.hutool.core.util.StrUtil;
import com.mall4j.cloud.common.constant.Auth;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import io.seata.core.context.RootContext;
import io.seata.spring.annotation.GlobalTransactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.stereotype.Component;
/**
* @author FrozenWatermelon
* @date 2020/11/27
*/
@Component
@ConditionalOnClass({RequestInterceptor.class, GlobalTransactional.class})
public class SeataRequestInterceptor implements RequestInterceptor {
private static final Logger logger = LoggerFactory.getLogger(SeataRequestInterceptor.class);
@Override
public void apply(RequestTemplate template) {
String currentXid = RootContext.getXID();
if (StrUtil.isNotBlank(currentXid) && !template.url().startsWith(Auth.CHECK_TOKEN_URI) && !template.url().startsWith(Auth.CHECK_RBAC_URI)) {
template.header(RootContext.KEY_XID, currentXid);
}
}
}
package com.mall4j.cloud.common.database.dto;
import com.github.pagehelper.IPage;
import com.mall4j.cloud.common.util.PrincipalUtil;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import java.util.Arrays;
/**
* @author FrozenWatermelon
* @date 2020/9/8
*/
public class PageDTO implements IPage {
public static final String ASC = "ASC";
public static final String DESC = "DESC";
/**
* 最大分页大小,如果分页大小大于500,则用500作为分页的大小。防止有人直接传入一个较大的数,导致服务器内存溢出宕机
*/
public static final Integer MAX_PAGE_SIZE = 500;
/**
* 当前页
*/
@NotNull(message = "pageNum 不能为空")
@Schema(description = "当前页" , requiredMode = Schema.RequiredMode.REQUIRED)
private Integer pageNum;
@NotNull(message = "pageSize 不能为空")
@Schema(description = "每页大小" , requiredMode = Schema.RequiredMode.REQUIRED)
private Integer pageSize;
@Schema(description = "排序字段数组,用逗号分割" )
private String[] columns;
@Schema(description = "排序字段方式,用逗号分割,ASC正序,DESC倒序" )
private String[] orders;
@Override
public Integer getPageNum() {
return pageNum;
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
@Override
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
if (pageSize > MAX_PAGE_SIZE) {
this.pageSize = MAX_PAGE_SIZE;
return;
}
this.pageSize = pageSize;
}
@Override
public String getOrderBy() {
return order(this.columns, this.orders);
}
public String[] getColumns() {
return columns;
}
public void setColumns(String[] columns) {
this.columns = columns;
}
public String[] getOrders() {
return orders;
}
public void setOrders(String[] orders) {
this.orders = orders;
}
public static String order(String[] columns, String[] orders) {
if (columns == null || columns.length == 0) {
return "";
}
StringBuilder stringBuilder = new StringBuilder();
for (int x = 0; x < columns.length; x++) {
String column = columns[x];
String order;
if (orders != null && orders.length > x) {
order = orders[x].toUpperCase();
if (!(order.equals(ASC) || order.equals(DESC))) {
throw new IllegalArgumentException("非法的排序策略:" + column);
}
}else {
order = ASC;
}
// 判断列名称的合法性,防止SQL注入。只能是【字母,数字,下划线】
if (PrincipalUtil.isField(column)) {
throw new IllegalArgumentException("非法的排序字段名称:" + column);
}
// 驼峰转换为下划线
column = humpConversionUnderscore(column);
if (x != 0) {
stringBuilder.append(", ");
}
stringBuilder.append("`").append(column).append("` ").append(order);
}
return stringBuilder.toString();
}
public static String humpConversionUnderscore(String value) {
StringBuilder stringBuilder = new StringBuilder();
char[] chars = value.toCharArray();
for (char character : chars) {
if (Character.isUpperCase(character)) {
stringBuilder.append("_");
character = Character.toLowerCase(character);
}
stringBuilder.append(character);
}
return stringBuilder.toString();
}
@Override
public String toString() {
return "PageDTO{" +
"pageNum=" + pageNum +
", pageSize=" + pageSize +
", columns=" + Arrays.toString(columns) +
", orders=" + Arrays.toString(orders) +
'}';
}
}
package com.mall4j.cloud.common.database.interceptor;
import com.mall4j.cloud.api.leaf.feign.SegmentFeignClient;
import com.mall4j.cloud.common.database.annotations.DistributedId;
import com.mall4j.cloud.common.exception.Mall4cloudException;
import com.mall4j.cloud.common.model.BaseModel;
import com.mall4j.cloud.common.response.ResponseEnum;
import com.mall4j.cloud.common.response.ServerResponseEntity;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* 分布式id生成
* 1. 分布式id是通过美团的leaf生成的,是需要与mall4cloud-leaf数据库 当中 leaf_alloc表中 biz_tag字段相关联的key
* 2. 为了注入分布式id更加方便,规定为DistributedId为注解的字段加入该字段
* @see DistributedId
* @author FrozenWatermelon
* @date 2020/9/9
*/
@Component
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class,Object.class})})
public class GeneratedKeyInterceptor implements Interceptor {
private static final Logger logger = LoggerFactory.getLogger(GeneratedKeyInterceptor.class);
/**
* 单个插入名称
*/
private static final String INSERT = "insert";
/**
* 单个插入名称
*/
private static final String SAVE = "save";
/**
* 批量插入名称
*/
private static final String BATCH_INSERT = "insertBatch";
/**
* 批量插入名称
*/
private static final String BATCH_SAVE = "saveBatch";
@Autowired
private SegmentFeignClient segmentFeignClient;
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];
// 获取 SQL
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
// 不是 insert 类型的跳过
if (SqlCommandType.INSERT != sqlCommandType) {
return invocation.proceed();
}
int one = 1;
// 获取参数
Object parameter = invocation.getArgs()[one];
// 找数据库中的对象
Object dbObject = findDbObject(parameter);
if (dbObject == null) {
return invocation.proceed();
}
// 插入
if (mappedStatement.getId().contains(INSERT) || mappedStatement.getId().contains(SAVE)){
generatedKey(dbObject);
}
// 批量插入
else if (mappedStatement.getId().contains(BATCH_INSERT) || mappedStatement.getId().contains(BATCH_SAVE)){
// 获取批量查询的参数并生成主键
if (parameter instanceof HashMap){
Object list = ((Map)parameter).get("list");
if (list instanceof ArrayList) {
for (Object o : (ArrayList) list) {
generatedKey(dbObject);
}
}
}
}
return invocation.proceed();
}
protected BaseModel findDbObject(Object parameterObj) {
if (parameterObj instanceof BaseModel) {
return (BaseModel)parameterObj;
} else if (parameterObj instanceof Map) {
for (Object val : ((Map<?, ?>) parameterObj).values()) {
if (val instanceof BaseModel) {
return (BaseModel)val;
}
}
}
return null;
}
/**
* 获取私有成员变量 ,并设置主键
* @param parameter 参数
*/
private void generatedKey(Object parameter) throws Throwable {
Field[] fieldList = parameter.getClass().getDeclaredFields();
for (Field field : fieldList) {
if (!field.getType().isAssignableFrom(Long.class)) {
break;
}
DistributedId annotation = field.getAnnotation(DistributedId.class);
if (annotation == null) {
break;
}
field.setAccessible(true);
if (field.get(parameter) != null) {
break;
}
ServerResponseEntity<Long> segmentIdResponseEntity = segmentFeignClient.getSegmentId(annotation.value());
if (segmentIdResponseEntity.isSuccess()) {
// 这里设置分布式id
field.set(parameter,segmentIdResponseEntity.getData());
} else {
logger.error("can't get distributed id !!!! ");
throw new Mall4cloudException(ResponseEnum.EXCEPTION);
}
}
}
/**
* Plugin.wrap生成拦截代理对象
*/
@Override
public Object plugin(Object o) {
if (o instanceof Executor) {
return Plugin.wrap(o, this);
} else {
return o;
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment