Commit 210282ef authored by 赵啸非's avatar 赵啸非

添加批量新增窗口业务接口

parent b7f5b4fe
package com.mortals.xhx.module.business.dao;
import com.mortals.framework.dao.ICRUDDao;
import com.mortals.framework.model.PageInfo;
import com.mortals.framework.model.Result;
import com.mortals.xhx.module.business.model.BusinessMatterEntity;
import com.mortals.xhx.module.business.model.BusinessMatterQuery;
import java.util.List;
/**
* 业务事项关联Dao
......@@ -13,4 +17,9 @@ import java.util.List;
public interface BusinessMatterDao extends ICRUDDao<BusinessMatterEntity,Long>{
Result<BusinessMatterEntity> getListByBusiness(BusinessMatterQuery params, PageInfo pageInfo);
Result<BusinessMatterEntity> getListByMatter(BusinessMatterQuery params, PageInfo pageInfo);
}
package com.mortals.xhx.module.business.dao.ibatis;
import com.mortals.framework.model.PageInfo;
import com.mortals.framework.model.Result;
import com.mortals.xhx.module.business.model.BusinessMatterQuery;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Repository;
import com.mortals.xhx.module.business.dao.BusinessMatterDao;
import com.mortals.xhx.module.business.model.BusinessMatterEntity;
import java.util.ArrayList;
import java.util.Date;
import com.mortals.framework.dao.ibatis.BaseCRUDDaoMybatis;
import java.util.List;
/**
* 业务事项关联DaoImpl DAO接口
*
* @author zxfei
* @date 2022-01-12
*/
* 业务事项关联DaoImpl DAO接口
*
* @author zxfei
* @date 2022-01-12
*/
@Repository("businessMatterDao")
public class BusinessMatterDaoImpl extends BaseCRUDDaoMybatis<BusinessMatterEntity,Long> implements BusinessMatterDao {
public class BusinessMatterDaoImpl extends BaseCRUDDaoMybatis<BusinessMatterEntity, Long> implements BusinessMatterDao {
@Override
public Result<BusinessMatterEntity> getListByBusiness(BusinessMatterQuery params, PageInfo pageInfo) {
Result<BusinessMatterEntity> result = new Result();
List list = null;
//判断是否计算总页数与总条数
int count =this.getSqlSession().selectOne(this.getSqlId("getListByBusinessCount"), params);
if (count == 0) {
list = new ArrayList();
} else if (pageInfo.getPrePageResult() == -1) {
list = this.getSqlSession().selectList(this.getSqlId("getListByBusiness"), params);
} else {
RowBounds rowBounds = new RowBounds(pageInfo.getBeginIndex(), pageInfo.getPrePageResult());
list = this.getSqlSession().selectList(this.getSqlId("getListByBusiness"), params, rowBounds);
}
pageInfo.setTotalResult(count);
result.setPageInfo(pageInfo);
result.setList(list);
return result;
}
@Override
public Result<BusinessMatterEntity> getListByMatter(BusinessMatterQuery params, PageInfo pageInfo) {
Result<BusinessMatterEntity> result = new Result();
List list = null;
//判断是否计算总页数与总条数
int count =this.getSqlSession().selectOne(this.getSqlId("getListByMatterCount"), params);
if (count == 0) {
list = new ArrayList();
} else if (pageInfo.getPrePageResult() == -1) {
list = this.getSqlSession().selectList(this.getSqlId("getListByMatter"), params);
} else {
RowBounds rowBounds = new RowBounds(pageInfo.getBeginIndex(), pageInfo.getPrePageResult());
list = this.getSqlSession().selectList(this.getSqlId("getListByMatter"), params, rowBounds);
}
pageInfo.setTotalResult(count);
result.setPageInfo(pageInfo);
result.setList(list);
return result;
}
}
package com.mortals.xhx.module.business.service;
import com.mortals.framework.model.Context;
import com.mortals.framework.model.PageInfo;
import com.mortals.framework.model.Result;
import com.mortals.framework.service.ICRUDService;
import com.mortals.xhx.module.business.model.BusinessMatterEntity;
import com.mortals.xhx.module.business.model.BusinessMatterQuery;
/**
* BusinessMatterService
*
......@@ -11,4 +16,11 @@ import com.mortals.xhx.module.business.model.BusinessMatterEntity;
*/
public interface BusinessMatterService extends ICRUDService<BusinessMatterEntity,Long>{
Result<BusinessMatterEntity> getListByBusiness(BusinessMatterQuery query, PageInfo pageInfo, Context context);
Result<BusinessMatterEntity> getListByMatter(BusinessMatterQuery query, PageInfo pageInfo, Context context);
}
\ No newline at end of file
package com.mortals.xhx.module.business.service.impl;
import com.mortals.framework.model.Context;
import com.mortals.framework.model.PageInfo;
import com.mortals.framework.model.Result;
import com.mortals.xhx.module.business.model.BusinessMatterQuery;
import org.springframework.stereotype.Service;
import com.mortals.framework.service.impl.AbstractCRUDServiceImpl;
import com.mortals.xhx.module.business.dao.BusinessMatterDao;
......@@ -13,5 +17,14 @@ import com.mortals.xhx.module.business.service.BusinessMatterService;
*/
@Service("businessMatterService")
public class BusinessMatterServiceImpl extends AbstractCRUDServiceImpl<BusinessMatterDao, BusinessMatterEntity, Long> implements BusinessMatterService {
@Override
public Result<BusinessMatterEntity> getListByBusiness(BusinessMatterQuery query, PageInfo pageInfo, Context context) {
return this.getDao().getListByBusiness(query,pageInfo);
}
@Override
public Result<BusinessMatterEntity> getListByMatter(BusinessMatterQuery query, PageInfo pageInfo, Context context) {
return this.getDao().getListByMatter(query,pageInfo);
}
}
\ No newline at end of file
......@@ -124,6 +124,9 @@ public class BusinessServiceImpl extends AbstractCRUDServiceImpl<BusinessDao, Bu
SiteBusinessEntity siteBusinessEntity = new SiteBusinessEntity();
BeanUtils.copyProperties(item, siteBusinessEntity, BeanUtil.getNullPropertyNames(item));
SiteEntity siteEntity = siteService.get(siteId);
if(ObjectUtils.isEmpty(siteEntity)){
throw new AppException("未找到相应站点!");
}
siteBusinessEntity.setSiteId(siteEntity.getId());
siteBusinessEntity.setSiteName(siteEntity.getSiteName());
siteBusinessEntity.setBusinessId(item.getId());
......
package com.mortals.xhx.module.business.web;
import com.alibaba.fastjson.JSONObject;
import com.mortals.framework.model.Context;
import com.mortals.framework.model.PageInfo;
import com.mortals.framework.model.Result;
import com.mortals.framework.web.BasePhpCRUDJsonMappingController;
import com.mortals.xhx.base.system.param.service.ParamService;
import com.mortals.xhx.module.business.model.BusinessMatterEntity;
import com.mortals.xhx.module.business.model.BusinessMatterQuery;
import com.mortals.xhx.module.business.service.BusinessMatterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
import static com.mortals.framework.ap.SysConstains.*;
/**
* 业务事项关联
*
......@@ -35,4 +45,78 @@ public class BusinessMatterController extends BasePhpCRUDJsonMappingController<B
}
@PostMapping(value = "businesslist")
public String getListByBusiness(@RequestBody BusinessMatterQuery query) {
Map<String, Object> model = new HashMap<>();
JSONObject ret = new JSONObject();
Context context = this.getContext();
String busiDesc = "查询" + this.getModuleDesc();
int code;
try {
PageInfo pageInfo = new PageInfo();
if (!ObjectUtils.isEmpty(query.getSize()) &&
!ObjectUtils.isEmpty(query)) {
pageInfo.setCurrPage(query.getPage());
pageInfo.setPrePageResult(query.getSize());
}
Result result = this.service.getListByBusiness(query, pageInfo, context);
model.put(KEY_RESULT_DATA, result.getList());
model.put(TOTAL, result.getPageInfo().getTotalResult());
model.put(PER_PAGE, result.getPageInfo().getPrePageResult());
model.put(CURRENT_PAGE, result.getPageInfo().getCurrPage());
model.put(LAST_PAGE, result.getPageInfo().getTotalPage());
code = this.doListAfter(query, model, context);
recordSysLog(request, busiDesc + " 【成功】");
} catch (Exception e) {
code = VALUE_RESULT_FAILURE;
this.doException(request, busiDesc, model, e);
}
this.init(model, context);
ret.put(KEY_RESULT_CODE, code);
ret.put(KEY_RESULT_MSG, model.remove(MESSAGE_INFO));
ret.put(KEY_RESULT_DICT, model.remove(KEY_RESULT_DICT));
ret.put(KEY_RESULT_DATA, model);
return ret.toJSONString();
}
@PostMapping(value = "matterlist")
public String getListByMatter(@RequestBody BusinessMatterQuery query) {
Map<String, Object> model = new HashMap<>();
JSONObject ret = new JSONObject();
Context context = this.getContext();
String busiDesc = "查询" + this.getModuleDesc();
int code;
try {
PageInfo pageInfo = new PageInfo();
if (!ObjectUtils.isEmpty(query.getSize()) &&
!ObjectUtils.isEmpty(query)) {
pageInfo.setCurrPage(query.getPage());
pageInfo.setPrePageResult(query.getSize());
}
Result result = this.service.getListByMatter(query, pageInfo, context);
model.put(KEY_RESULT_DATA, result.getList());
model.put(TOTAL, result.getPageInfo().getTotalResult());
model.put(PER_PAGE, result.getPageInfo().getPrePageResult());
model.put(CURRENT_PAGE, result.getPageInfo().getCurrPage());
model.put(LAST_PAGE, result.getPageInfo().getTotalPage());
code = this.doListAfter(query, model, context);
recordSysLog(request, busiDesc + " 【成功】");
} catch (Exception e) {
code = VALUE_RESULT_FAILURE;
this.doException(request, busiDesc, model, e);
}
this.init(model, context);
ret.put(KEY_RESULT_CODE, code);
ret.put(KEY_RESULT_MSG, model.remove(MESSAGE_INFO));
ret.put(KEY_RESULT_DICT, model.remove(KEY_RESULT_DICT));
ret.put(KEY_RESULT_DATA, model);
return ret.toJSONString();
}
}
\ No newline at end of file
......@@ -9,7 +9,7 @@
<setting name="useGeneratedKeys" value="false" />
<setting name="defaultExecutorType" value="REUSE" />
<!-- 是否开始sql日志控制台打印 -->
<setting name="logImpl" value="STDOUT_LOGGING" />
<!-- <setting name="logImpl" value="STDOUT_LOGGING" />-->
</settings>
<plugins>
<plugin interceptor="com.mortals.framework.thirty.mybatis.MortalsPagePlugin">
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"mybatis-3-mapper.dtd">
<mapper namespace="com.mortals.xhx.module.business.dao.ibatis.BusinessMatterDaoImpl">
<!-- 获取业务列表 -->
<select id="getListByBusinessCount" parameterType="paramDto" resultType="int">
SELECT
count(1)
FROM
mortals_sys_business_matter as a
WHERE
id IN ( SELECT MAX( id ) FROM mortals_sys_business_matter as a WHERE
a.siteId = #{siteId}
GROUP BY
a.siteBusinessId
)
</select>
<select id="getListByBusiness" parameterType="paramDto" resultMap="BusinessMatterEntity-Map">
SELECT
count(1)
FROM
mortals_sys_business_matter as a
WHERE
id IN ( SELECT MAX( id ) FROM mortals_sys_business_matter as a WHERE
a.siteId = #{siteId}
GROUP BY
a.siteBusinessId
)
</select>
<!-- 获取事项列表 -->
<select id="getListByMatterCount" parameterType="paramDto" resultType="int">
SELECT
<include refid="_columns"/>
FROM
mortals_sys_business_matter as a
WHERE
id IN ( SELECT MAX( id ) FROM mortals_sys_business_matter as a WHERE
a.siteId = #{siteId}
GROUP BY
a.matterId
)
</select>
<select id="getListByMatter" parameterType="paramDto" resultMap="BusinessMatterEntity-Map">
SELECT
<include refid="_columns"/>
FROM
mortals_sys_business_matter as a
WHERE
id IN ( SELECT MAX( id ) FROM mortals_sys_business_matter as a WHERE
a.siteId = #{siteId}
GROUP BY
a.matterId
)
</select>
</mapper>
\ No newline at end of file
......@@ -11,6 +11,14 @@ Content-Type: application/json
"size":10
}
###业务事项关联列表
POST {{baseUrl}}/business/matter/businesslist
Content-Type: application/json
{
"siteId":2
}
###业务事项关联更新与保存
POST {{baseUrl}}/business/matter/save
......
if not exist "%JAVA_HOME%\bin\jps.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! jdk8 or later is better! & EXIT /B 1
setlocal
set "PATH=%JAVA_HOME%\bin;%PATH%"
echo killing server
for /f "tokens=1" %%i in ('jps -m ^| find "@project.artifactId@"') do ( taskkill /F /PID %%i )
echo Done!
echo off
if not exist "%JAVA_HOME%\bin\java.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! jdk8 or later is better! & EXIT /B 1
set "JAVA=%JAVA_HOME%\bin\java.exe"
set BASEDIR=%~dp0
set BASEDIR="%BASEDIR:~0,-5%"
set PROJECT_NAME=@project.artifactId@
set APP_NAME=%PROJECT_NAME%-@project.version@.jar
set LOG_PATH="%BASEDIR%@profiles.log.path@/%PROJECT_NAME%"
if not exist "%LOG_PATH%" md "%LOG_PATH%"
set GC_PATH=%LOG_PATH%/gc.log
set HS_ERR_PATH=%LOG_PATH%PORT%-hs_err.log
set HEAP_DUMP_PATH=%LOG_PATH%/heap_dump.hprof
set TEMP_PATH=%LOG_PATH%/temp/
if not exist "%TEMP_PATH%" md "%TEMP_PATH%"
rem jvm启动参数
set JAVA_OPTS=-Xms512M -Xmx512M -Xss256K -XX:+UseAdaptiveSizePolicy -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:GCTimeRatio=39 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryError
set JAVA_OPTS=%JAVA_OPTS% -XX:+PrintGCDateStamps -Xloggc:%GC_PATH%
set JAVA_OPTS=%JAVA_OPTS% -XX:ErrorFile=%HS_ERR_PATH%
set JAVA_OPTS=%JAVA_OPTS% -XX:HeapDumpPath=%HEAP_DUMP_PATH%
rem jvm config
set JVM_CONFIG=%JVM_CONFIG% -Dapp.name=%PROJECT_NAME%
set JVM_CONFIG=%JVM_CONFIG% -Dapp.port=%PORT%
set JVM_CONFIG=%JVM_CONFIG% -Djava.io.tmpdir=%TEMP_PATH%
set JVM_CONFIG=%JVM_CONFIG% -Dbasedir=%BASEDIR%
set JVM_CONFIG=%JVM_CONFIG% -Dloader.path=file://%BASEDIR%/conf,file://%BASEDIR%/lib
set DEBUG_OPTS=
if ""%1"" == ""debug"" (
set DEBUG_OPTS= -Xloggc:../logs/gc.log -verbose:gc -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=../logs
goto debug
)
set JMX_OPTS=
if ""%1"" == ""jmx"" (
set JMX_OPTS= -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9888 -Dcom.sun.management.jmxremote.ssl=FALSE -Dcom.sun.management.jmxremote.authenticate=FALSE
goto jmx
)
set "LOG_OPTS=--logging.config=%BASEDIR%/conf/logback-spring.xml"
echo "Starting the %APP_NAME%"
"%JAVA%" %JAVA_OPTS% -server %JVM_CONFIG% -jar %BASEDIR%/boot/%APP_NAME%
echo ""%JAVA%" %JAVA_OPTS% -server %JVM_CONFIG% -jar %BASEDIR%/boot/%APP_NAME% "
goto end
:debug
echo "debug"
"%JAVA%" -Xms512m -Xmx512m -server %DEBUG_OPTS% %JVM_CONFIG% -jar ../boot/%APP_NAME%
goto end
:jmx
"%JAVA%" -Xms512m -Xmx512m -server %JMX_OPTS% %JVM_CONFIG% -jar ../boot/%APP_NAME%
goto end
:end
pause
#!/bin/sh
PORT="@profiles.server.port@"
BASEDIR=`dirname $0`/..
BASEDIR=`(cd "$BASEDIR"; pwd)`
PROJECT_NAME="@project.artifactId@";
MAIN_CLASS="$PROJECT_NAME-@project.version@.jar";
LOG_PATH="@profiles.log.path@/$PROJECT_NAME"
GC_PATH=$LOG_PATH/$PORT"-gc.log"
HS_ERR_PATH=$LOG_PATH/$PORT"-hs_err.log"
HEAP_DUMP_PATH=$LOG_PATH/$PORT"-heap_dump.hprof"
TEMP_PATH=$LOG_PATH/temp/
SUCCESS=0
FAIL=9
if [ ! -n "$PORT" ]; then
echo $"Usage: $0 {port}"
exit $FAIL
fi
if [ ! -d $LOG_PATH ];
then
mkdir -p $LOG_PATH;
fi
if [ ! -d $TEMP_PATH ];
then
mkdir -p $TEMP_PATH;
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD=`which java > /dev/null 2>&1`
echo "Error: JAVA_HOME is not defined correctly."
exit $ERR_NO_JAVA
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "We cannot execute $JAVACMD"
exit $ERR_NO_JAVA
fi
if [ -e "$BASEDIR" ]
then
JAVA_OPTS="-Xms512M -Xmx1024M -Xss256K -XX:+UseAdaptiveSizePolicy -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:GCTimeRatio=39 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:$GC_PATH -XX:+HeapDumpOnOutOfMemoryError -XX:ErrorFile=$HS_ERR_PATH -XX:HeapDumpPath=$HEAP_DUMP_PATH"
fi
CLASSPATH=$CLASSPATH_PREFIX:
EXTRA_JVM_ARGUMENTS=""
cd "$BASEDIR/boot";
echo "starting application $PROJECT_NAME......"
exec "$JAVACMD" $JAVA_OPTS \
$EXTRA_JVM_ARGUMENTS \
-Dapp.name="$PROJECT_NAME" \
-Dapp.port="$PORT" \
-Dbasedir="$BASEDIR" \
-Djava.io.tmpdir=$TEMP_PATH \
-Dloader.path="file://$BASEDIR/conf,file://$BASEDIR/lib" \
-jar $MAIN_CLASS \
> /dev/null &
for i in {1..60}
do
jcpid=`ps -ef | grep -v "grep" | grep "$MAIN_CLASS" | grep "app.port=$PORT" | sed -n '1P' | awk '{print $2}'`
if [ $jcpid ]; then
echo "The $PROJECT_NAME start finished, PID is $jcpid"
exit $SUCCESS
else
echo "starting the application .. $i"
sleep 1
fi
done
echo "$PROJECT_NAME start failure!"
#! /bin/sh
PORT="@profiles.server.port@"
BASEDIR=`dirname $0`
BASEDIR=`(cd "$BASEDIR"; pwd)`
PROJECT_NAME="@project.artifactId@"
MAIN_CLASS="$PROJECT_NAME";
if [ ! -n "$PORT" ]; then
echo $"Usage: $0 {port}"
exit $FAIL
fi
echo "stoping application $PROJECT_NAME......"
jcpid=`ps -ef | grep -v "grep" | grep "$MAIN_CLASS" | grep "app.port=$PORT" | sed -n '1P' | awk '{print $2}'`
if [ -z $jcpid ]; then
echo "$PROJECT_NAME is not started or has been stopped!"
else
curl -X POST -i -u $SECURITY_USERNAME:$SECURITY_PASSWORD http://127.0.0.1:$PORT/xxx_manager/shutdown
for i in {1..60}
do
jcpid=`ps -ef | grep -v "grep" | grep "$MAIN_CLASS" | grep "app.port=$PORT" | sed -n '1P' | awk '{print $2}'`
if [ -z $jcpid ]; then
echo "$PROJECT_NAME has been stopped!"
break
else
echo "stoping the application .. $i"
sleep 1
fi
done
jcpid=`ps -ef | grep -v "grep" | grep "$MAIN_CLASS" | grep "app.port=$PORT" | sed -n '1P' | awk '{print $2}'`
if [ $jcpid ]; then
[ -z $jcpid ] || kill -15 $jcpid
for i in {1..30}
do
jcpid=`ps -ef | grep -v "grep" | grep "$MAIN_CLASS" | grep "app.port=$PORT" | sed -n '1P' | awk '{print $2}'`
if [ -z $jcpid ]; then
echo "$PROJECT_NAME has been stopped!"
break
else
echo "stoping the application .. $i"
sleep 1
fi
done
fi
jcpid=`ps -ef | grep -v "grep" | grep "$MAIN_CLASS" | grep "app.port=$PORT" | sed -n '1P' | awk '{print $2}'`
if [ $jcpid ]; then
[ -z $jcpid ] || kill -9 $jcpid
[ $? -eq 0 ] && echo "Stop $PROJECT_NAME OK!" || echo "Stop $PROJECT_NAME Fail!"
fi
fi
package com.mortals.xhx;
import com.mortals.framework.springcloud.boot.BaseWebApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.ImportResource;
@SpringBootApplication(scanBasePackages = {"com.mortals"})
@ServletComponentScan("com.mortals")
@ImportResource(locations = {"classpath:config/spring-config.xml"})
public class ManagerApplication extends BaseWebApplication {
public static void main(String[] args) {
SpringApplication.run(ManagerApplication.class, args);
}
}
package com.mortals.xhx.base.framework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Operlog {
String msg() default "";
String params() default "";
}
package com.mortals.xhx.base.framework.aspect;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.mortals.framework.service.ILogService;
import com.mortals.framework.service.impl.FileLogServiceImpl;
import com.mortals.xhx.base.system.oper.service.OperLogService;
/**
* 操作日志记录
*
* @author: zxfei
* @date: 2021/11/5 13:28
*/
@Component
public class OperlogAspect extends FileLogServiceImpl implements ILogService {
private final static Logger logger = LoggerFactory.getLogger(OperlogAspect.class);
@Autowired
private OperLogService operLogService;
@Override
public void doHandlerLog(String platformMark, Long userId, String userName, String loginName, String requestUrl,
String content, String ip, Date logDate) {
super.doHandlerLog(platformMark, userId, userName, loginName, requestUrl, content, ip, logDate);
operLogService.insertOperLog(ip, requestUrl, userId, userName, loginName, content);
}
@Override
public void doHandlerLog(String platformMark, String loginName, String requestUrl, String content, String ip) {
// operLogService.insertOperLog(ip, requestUrl, null, "", loginName,
// content);
this.doHandlerLog(platformMark, null, "", loginName, requestUrl, content, ip, new Date());
}
@Pointcut("execution(public * com.mortals.xhx..*Controller.*(..))")
public void accessLog() {
}
@Before("accessLog()")
public void doBefore(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// url
logger.info("ip[{}]url[{}]", request.getRemoteAddr(), request.getRequestURL());
// 参数第1和第2个参数为HttpServletRequest request, HttpServletResponse
// response
if (joinPoint.getArgs().length > 2) {
logger.info("args={}", joinPoint.getArgs()[2]);
} else {
logger.info("args={}", joinPoint.getArgs());
}
}
@AfterReturning(returning = "object", pointcut = "accessLog()")
public void doAfterReturning(Object object) {
if (null != object) {
logger.info("response={}", object.toString());
}
}
}
package com.mortals.xhx.base.framework.aspect;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.mortals.framework.common.code.YesNo;
import com.mortals.framework.service.ICacheService;
import com.mortals.framework.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringEscapeUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.core.io.InputStreamSource;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Spring boot 控制器 请求日志,方便代码调试
*
* @author zxfei
*/
@Slf4j
@Aspect
@Configuration(proxyBeanMethods = false)
@Profile({"develop1"})
public class RequestLogAspect {
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
/**
* AOP 环切 控制器 R 返回值
*
* @param point JoinPoint
* @return Object
* @throws Throwable 异常
*/
@Around("execution(* com.mortals..*Controller.*(..)) || execution(* com.mortals..*Feign.*(..))")
public Object aroundApi(ProceedingJoinPoint point) throws Throwable {
MethodSignature ms = (MethodSignature) point.getSignature();
Method method = ms.getMethod();
Object[] args = point.getArgs();
// 请求参数处理
final Map<String, Object> paraMap = new HashMap<>(16);
for (int i = 0; i < args.length; i++) {
// 读取方法参数
MethodParameter methodParam = getMethodParameter(method, i);
// PathVariable 参数跳过
PathVariable pathVariable = methodParam.getParameterAnnotation(PathVariable.class);
if (pathVariable != null) {
continue;
}
RequestBody requestBody = methodParam.getParameterAnnotation(RequestBody.class);
String parameterName = methodParam.getParameterName();
if (parameterName.equals("binder")) {
continue;
}
Object value = args[i];
// 如果是body的json则是对象
if (requestBody != null && value != null) {
paraMap.putAll(BeanMap.create(value));
continue;
}
// 处理 List
if (value instanceof List) {
value = ((List) value).get(0);
}
// 处理 参数
if (value instanceof HttpServletRequest) {
paraMap.putAll(((HttpServletRequest) value).getParameterMap());
} else if (value instanceof WebRequest) {
paraMap.putAll(((WebRequest) value).getParameterMap());
} else if (value instanceof MultipartFile) {
MultipartFile multipartFile = (MultipartFile) value;
String name = multipartFile.getName();
String fileName = multipartFile.getOriginalFilename();
paraMap.put(name, fileName);
} else if (value instanceof HttpServletResponse) {
} else if (value instanceof InputStream) {
} else if (value instanceof InputStreamSource) {
} else if (value instanceof List) {
List<?> list = (List<?>) value;
AtomicBoolean isSkip = new AtomicBoolean(false);
for (Object o : list) {
if ("StandardMultipartFile".equalsIgnoreCase(o.getClass().getSimpleName())) {
isSkip.set(true);
break;
}
}
if (isSkip.get()) {
paraMap.put(parameterName, "此参数不能序列化为json");
continue;
}
} else {
// 参数名
RequestParam requestParam = methodParam.getParameterAnnotation(RequestParam.class);
String paraName;
if (requestParam != null && StringUtils.isNotBlank(requestParam.value())) {
paraName = requestParam.value();
} else {
paraName = methodParam.getParameterName();
}
paraMap.put(paraName, value);
}
}
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = (requestAttributes == null) ? null : ((ServletRequestAttributes) requestAttributes).getRequest();
String requestURI = Objects.requireNonNull(request).getRequestURI();
String requestMethod = request.getMethod();
// 构建成一条长 日志,避免并发下日志错乱
StringBuilder beforeReqLog = new StringBuilder(300);
// 日志参数
List<Object> beforeReqArgs = new ArrayList<>();
//beforeReqLog.append("\n\n================ Request Start ================\n");
// 打印路由
beforeReqLog.append("Request ===> {}: {}");
beforeReqArgs.add(requestMethod);
beforeReqArgs.add(requestURI);
// 请求参数
if (paraMap.isEmpty()) {
beforeReqLog.append("\n");
} else {
beforeReqLog.append(" Parameters: {}\n");
beforeReqArgs.add(JSON.toJSONString(paraMap, SerializerFeature.DisableCircularReferenceDetect));
}
// 打印请求头
/* Enumeration<String> headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
String headerName = headers.nextElement();
String headerValue = request.getHeader(headerName);
beforeReqLog.append("===Headers=== {} : {}\n");
beforeReqArgs.add(headerName);
beforeReqArgs.add(headerValue);
}*/
//beforeReqLog.append("================ Request End ================\n");
// 打印执行时间
long startNs = System.nanoTime();
log.info(beforeReqLog.toString(), beforeReqArgs.toArray());
// aop 执行后的日志
StringBuilder afterReqLog = new StringBuilder(200);
// 日志参数
List<Object> afterReqArgs = new ArrayList<>();
//afterReqLog.append("\n\n================ Response Start ================\n");
try {
Object result = point.proceed();
// 打印返回结构体
afterReqLog.append("Response===> {}\n");
afterReqArgs.add(StringEscapeUtils.unescapeJava(JSON.toJSONString(result, SerializerFeature.DisableCircularReferenceDetect)));
return result;
} finally {
long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
afterReqLog.append("=== {}: {} ({} ms)\n");
afterReqArgs.add(requestMethod);
afterReqArgs.add(requestURI);
afterReqArgs.add(tookMs);
//afterReqLog.append("================ Response End ================\n");
log.info(afterReqLog.toString(), afterReqArgs.toArray());
}
}
/**
* 获取方法参数信息
*
* @param method 方法
* @param parameterIndex 参数序号
* @return {MethodParameter}
*/
public MethodParameter getMethodParameter(Method method, int parameterIndex) {
MethodParameter methodParameter = new SynthesizingMethodParameter(method, parameterIndex);
methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
return methodParameter;
}
public static void main(String[] args) {
String str = "execution(* com.mortals..*Controller.*(..) && " +
"(@within(org.springframework.stereotype.Controller) || " +
"@within(org.springframework.web.bind.annotation.RestController))";
System.out.println(str);
}
}
\ No newline at end of file
package com.mortals.xhx.base.framework.config;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.PostConstruct;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import com.mortals.framework.util.StringUtils;
@Configuration
public class InterceptorConfig {
/**
* cookie的密钥
*/
@Value("${cookie.key}")
private String securityKey;
/**
* 不需要登录的URL地址,多个用逗号分隔
*/
@Value("${application.auth.unloginUrl}")
private String uncheckLoginUrl;
/**
* 不需要鉴权的URL地址,多个用逗号分隔
*/
@Value("${application.auth.uncheckUrl}")
private String uncheckAuthUrl;
private Set<String> uncheckLoginUrls = new HashSet<>();
private Set<String> uncheckAuthUrls = new HashSet<>();
/**
* 有后缀占位符的不检验地址,如/spi/user/*
*/
private Set<String> uncheckAuthUrlsSuffix = new HashSet<>();
public String getSecurityKey() {
return securityKey;
}
public Set<String> getUncheckLoginUrls() {
return uncheckLoginUrls;
}
public Set<String> getUncheckAuthUrls() {
return uncheckAuthUrls;
}
/**
* 校验地址是否需要权限
*
* @param url
* @return true:需要,false:不需要
*/
public boolean needCheckAuth(String url) {
if (uncheckAuthUrls.contains(url)) {
return false;
}
for (String regexUrl : uncheckAuthUrlsSuffix) {// 支持*结尾
if (url.startsWith(regexUrl)) {
return false;
}
}
return true;
}
@PostConstruct
public void convert() {
uncheckLoginUrls.clear();
uncheckLoginUrls.addAll(StringUtils.converStr2Set(uncheckLoginUrl));
uncheckAuthUrls.clear();
uncheckAuthUrls.addAll(StringUtils.converStr2Set(uncheckAuthUrl));
uncheckAuthUrlsSuffix.clear();
for (String url : uncheckAuthUrls) {
int index = url.indexOf("*");// 支持*结尾
if (index != -1) {
uncheckAuthUrlsSuffix.add(url.substring(0, index));
}
}
}
}
package com.mortals.xhx.base.framework.config;
import com.mortals.framework.springcloud.config.mybatis.AbstractMybatisConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
//@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisConfiguration extends AbstractMybatisConfiguration {
private static Log logger = LogFactory.getLog(MybatisConfiguration.class);
// 配置类型别名
@Value("${spring.application.name}")
private String name;
// 配置类型别名
@Value("${mybatis.root-path}")
private String rootPath;
// 配置类型别名
@Value("${mybatis.type-aliases-package}")
private String typeAliasesPackage;
// 配置mapper的扫描,找到所有的mapper.xml映射文件
@Value("${mybatis.mapper-locations}")
private String mapperLocations;
// 加载全局的配置文件
@Value("${mybatis.config-location}")
private String configLocation;
// 提供SqlSeesion
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactoryBean(@Qualifier("dataSource") DataSource dataSource) {
return super.getSqlSessionFactoryBean(dataSource);
}
@Override
public String getTypeAliasesRootPackage() {
return rootPath;
}
@Override
public String getSqlMappers() {
return mapperLocations;
}
@Override
public String getMybatisConfig() {
return configLocation;
}
@Override
public String getTypeAliasesPackage() {
logger.info("typeAliasesPackage:"+typeAliasesPackage);
return typeAliasesPackage;
}
}
\ No newline at end of file
package com.mortals.xhx.base.framework.exception;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSONObject;
import com.mortals.framework.exception.AppException;
/**
* 统一异常处理
*/
@ControllerAdvice
public class ExceptionHandle {
private final static Logger log = LoggerFactory.getLogger(ExceptionHandle.class);
public static final String KEY_RESULT_CODE = "code";
public static final String KEY_RESULT_MSG = "msg";
public static final String KEY_RESULT_DATA = "data";
public static final int VALUE_RESULT_FAILURE = -1;
@ExceptionHandler(value = Exception.class)
@ResponseBody
public String handle(Exception e) {
JSONObject ret = new JSONObject();
ret.put(KEY_RESULT_CODE, VALUE_RESULT_FAILURE);
if (e instanceof AppException) {
StackTraceElement stack = e.getStackTrace()[0];
log.error("[business error]=========stack message[{}],[{},method:{},line{}][{}]", e.getMessage(),
stack.getClassName(), stack.getMethodName(), stack.getLineNumber(), e.getClass().getName());
AppException ex = (AppException) e;
ret.put(KEY_RESULT_MSG, ex.getMessage());
} else {
log.error("[system error]", e);
ret.put(KEY_RESULT_MSG, "unknown exception!");
}
return ret.toJSONString();
}
}
package com.mortals.xhx.base.login.interceptor;
import com.mortals.xhx.base.framework.config.InterceptorConfig;
import com.mortals.framework.ap.CookieService;
import com.mortals.framework.ap.SysConstains;
import com.mortals.framework.model.CookieInfo;
import com.mortals.framework.service.ICacheService;
import com.mortals.framework.util.AESUtil;
import com.mortals.framework.util.HttpUtil;
import com.mortals.framework.util.StringUtils;
import com.mortals.framework.web.interceptor.BaseInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Order(1)
@Component
public class AuthJsonInterceptor extends BaseInterceptor {
@Autowired
private InterceptorConfig config;
@Autowired
private ICacheService cacheService;
@Override
public int getOrder() {
return Integer.MAX_VALUE - 9;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
try {
String uri = request.getServletPath();
if (config.needCheckAuth(uri)) {
boolean auth = this.checkAuth(request, uri, config.getSecurityKey());
if (!auth) {
CookieInfo cookie = CookieService.getLoginCookie(request, config.getSecurityKey());
if (cookie == null || cookie.getUser() == null) {
writeJsonResponse(response, HttpServletResponse.SC_FORBIDDEN, "用户未登录或登录失效,请重新登录");
return false;
} else if (cookie.getUser().isAdmin()) {
return super.preHandle(request, response, handler);
} else {
writeJsonResponse(response, HttpServletResponse.SC_UNAUTHORIZED, "用户无该操作权限");
return false;
}
}
}
} catch (Exception e) {
logger.error("权限校验拦截请求处理异常-->" + e.getMessage());
writeJsonResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "用户权限校验异常");
return false;
}
return super.preHandle(request, response, handler);
}
private boolean checkAuth(HttpServletRequest request, String requestUrl, String securityKey) throws Exception {
int code = requestUrl.hashCode() & (Integer.MAX_VALUE - 1);
String cacheKey = HttpUtil.getCookieValue(request, SysConstains.COOKIE_MENU);
if (StringUtils.isEmpty(cacheKey)) {
return false;
}
String menuUrl = cacheService.get(cacheKey);
if (StringUtils.isEmpty(menuUrl)) {
return false;
}
menuUrl = AESUtil.decrypt(menuUrl, securityKey);
String codes = "," + menuUrl + ",";
String codeKey = "," + code + ",";
if (codes.indexOf(codeKey) != -1) {
return true;
}
return false;
}
}
package com.mortals.xhx.base.login.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.mortals.xhx.base.framework.config.InterceptorConfig;
import org.springframework.beans.factory.annotation.Autowired;
import com.mortals.framework.ap.CookieService;
import com.mortals.framework.model.CookieInfo;
import com.mortals.framework.service.ITokenService;
import com.mortals.framework.web.interceptor.BaseInterceptor;
//@Component
public class LoginJsonInterceptor extends BaseInterceptor {
@Autowired(required = false)
private ITokenService tokenService;
@Autowired
private InterceptorConfig config;
@Override
public int getOrder() {
return Integer.MAX_VALUE - 10;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
try {
String uri = request.getServletPath();
if (config.needCheckAuth(uri)) {
CookieInfo cookie = CookieService.getLoginCookie(request, config.getSecurityKey());
boolean expire = true;
try {
if (cookie == null || cookie.getUser() == null
|| (cookie != null && tokenService != null && tokenService.isExpireToken(cookie))) {
expire = true;
} else {
expire = false;
}
} catch (Exception e) {
logger.warn("校验cookie是否过期异常-->原因:" + e.getMessage());
}
// 判断用户是否登录
if (expire) {
CookieService.deleteLoginCookie(request, response);
writeJsonResponse(response, HttpServletResponse.SC_FORBIDDEN, "用户未登录或登录失效,请重新登录111333");
return false;
}
}
} catch (Exception e) {
logger.error("登录校验拦截请求处理异常-->" + e.getMessage());
writeJsonResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "用户登录校验异常");
return false;
}
return super.preHandle(request, response, handler);
}
}
package com.mortals.xhx.base.login.service;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Random;
import javax.imageio.ImageIO;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* gif动态图验证码图片生成器
*
*/
public class GifSecurityImage {
private static Log logger = LogFactory.getLog(GifSecurityImage.class);
public static BufferedImage createImage(String securityCode) {
int codeLength = securityCode.length();
int fSize = 14;
int fWidth = fSize + 1;
int width = codeLength * fWidth + 6;
int height = fSize * 2 + 1;
BufferedImage image = new BufferedImage(width, height, 1);
Graphics g = image.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
g.setColor(Color.LIGHT_GRAY);
g.setFont(new Font("Arial", 1, height - 2));
Random rand = new Random();
// 画点
g.setColor(Color.LIGHT_GRAY);
for (int i = 0; i < codeLength * 10; i++) {
g.setColor(randomColor());
int x = rand.nextInt(width);
int y = rand.nextInt(height);
g.drawOval(x, y, 1, 1);
}
for (int i = 0; i < 5; i++) {
g.setColor(randomColor());
int x = rand.nextInt(width - 1);
int y = rand.nextInt(height - 1);
int xl = rand.nextInt(6) + 1;
int yl = rand.nextInt(12) + 1;
g.drawLine(x, y, xl, yl);
}
// 写字
g.setFont(new Font("Georgia", 1, fSize));
for (int i = 0; i < codeLength; i++) {
int codeY = height - 8 - rand.nextInt(5);
int codeX = width / 4 * i + (width / 4 - fSize) / 2;
g.drawString(String.valueOf(securityCode.charAt(i)), codeX, codeY);
}
g.dispose();
return image;
}
public static byte[] createGifImage(String securityCode) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
AnimatedGifEncoder gifEncoder = new AnimatedGifEncoder();
gifEncoder.setRepeat(0);
gifEncoder.start(outputStream);
for (int j = 0; j < 4; j++) {
gifEncoder.setDelay(200); // 设置播放的延迟时间
gifEncoder.addFrame(createImage(securityCode));
}
gifEncoder.finish();
return outputStream.toByteArray();
}
private static Color randomColor() {
Random random = new Random();
int r = random.nextInt(256);
int g = random.nextInt(256);
int b = random.nextInt(256);
return new Color(r, g, b);
}
public static ByteArrayInputStream getImageAsInputStream(String securityCode) {
BufferedImage image = createImage(securityCode);
return convertImageToStream(image);
}
private static ByteArrayInputStream convertImageToStream(BufferedImage image) {
ByteArrayInputStream inputStream = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
byte[] bts = bos.toByteArray();
inputStream = new ByteArrayInputStream(bts);
ImageIO.write(image, "JPG", bos);
} catch (Exception e) {
logger.debug("转换图片数据流异常-->" + e.getMessage());
}
return inputStream;
}
}
package com.mortals.xhx.base.login.service;
import java.io.IOException;
import java.io.OutputStream;
//==============================================================================
// Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott.
// K Weiner 12/00
class LZWEncoder {
private static final int EOF = -1;
private int imgW, imgH;
private byte[] pixAry;
private int initCodeSize;
private int remaining;
private int curPixel;
// GIFCOMPR.C - GIF Image compression routines
//
// Lempel-Ziv compression based on 'compress'. GIF modifications by
// David Rowley (mgardi@watdcsu.waterloo.edu)
// General DEFINEs
static final int BITS = 12;
static final int HSIZE = 5003; // 80% occupancy
// GIF Image compression - modified 'compress'
//
// Based on: compress.c - File compression ala IEEE Computer, June 1984.
//
// By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
// Jim McKie (decvax!mcvax!jim)
// Steve Davies (decvax!vax135!petsd!peora!srd)
// Ken Turkowski (decvax!decwrl!turtlevax!ken)
// James A. Woods (decvax!ihnp4!ames!jaw)
// Joe Orost (decvax!vax135!petsd!joe)
int n_bits; // number of bits/code
int maxbits = BITS; // user settable max # bits/code
int maxcode; // maximum code, given n_bits
int maxmaxcode = 1 << BITS; // should NEVER generate this code
int[] htab = new int[HSIZE];
int[] codetab = new int[HSIZE];
int hsize = HSIZE; // for dynamic table sizing
int free_ent = 0; // first unused entry
// block compression parameters -- after all codes are used up,
// and compression rate changes, start over.
boolean clear_flg = false;
// Algorithm: use open addressing double hashing (no chaining) on the
// prefix code / next character combination. We do a variant of Knuth's
// algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
// secondary probe. Here, the modular division first probe is gives way
// to a faster exclusive-or manipulation. Also do block compression with
// an adaptive reset, whereby the code table is cleared when the compression
// ratio decreases, but after the table fills. The variable-length output
// codes are re-sized at this point, and a special CLEAR code is generated
// for the decompressor. Late addition: construct the table according to
// file size for noticeable speed improvement on small files. Please direct
// questions about this implementation to ames!jaw.
int g_init_bits;
int ClearCode;
int EOFCode;
// output
//
// Output the given code.
// Inputs:
// code: A n_bits-bit integer. If == -1, then EOF. This assumes
// that n_bits =< wordsize - 1.
// Outputs:
// Outputs code to the file.
// Assumptions:
// Chars are 8 bits long.
// Algorithm:
// Maintain a BITS character long buffer (so that 8 codes will
// fit in it exactly). Use the VAX insv instruction to insert each
// code in turn. When the buffer fills up empty it and start over.
int cur_accum = 0;
int cur_bits = 0;
int masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF,
0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
// Number of characters so far in this 'packet'
int a_count;
// Define the storage for the packet accumulator
byte[] accum = new byte[256];
// ----------------------------------------------------------------------------
LZWEncoder(int width, int height, byte[] pixels, int color_depth) {
imgW = width;
imgH = height;
pixAry = pixels;
initCodeSize = Math.max(2, color_depth);
}
// Add a character to the end of the current packet, and if it is 254
// characters, flush the packet to disk.
void char_out(byte c, OutputStream outs) throws IOException {
accum[a_count++] = c;
if (a_count >= 254)
flush_char(outs);
}
// Clear out the hash table
// table clear for block compress
void cl_block(OutputStream outs) throws IOException {
cl_hash(hsize);
free_ent = ClearCode + 2;
clear_flg = true;
output(ClearCode, outs);
}
// reset code table
void cl_hash(int hsize) {
for (int i = 0; i < hsize; ++i)
htab[i] = -1;
}
void compress(int init_bits, OutputStream outs) throws IOException {
int fcode;
int i /* = 0 */;
int c;
int ent;
int disp;
int hsize_reg;
int hshift;
// Set up the globals: g_init_bits - initial number of bits
g_init_bits = init_bits;
// Set up the necessary values
clear_flg = false;
n_bits = g_init_bits;
maxcode = MAXCODE(n_bits);
ClearCode = 1 << (init_bits - 1);
EOFCode = ClearCode + 1;
free_ent = ClearCode + 2;
a_count = 0; // clear packet
ent = nextPixel();
hshift = 0;
for (fcode = hsize; fcode < 65536; fcode *= 2)
++hshift;
hshift = 8 - hshift; // set hash code range bound
hsize_reg = hsize;
cl_hash(hsize_reg); // clear hash table
output(ClearCode, outs);
outer_loop: while ((c = nextPixel()) != EOF) {
fcode = (c << maxbits) + ent;
i = (c << hshift) ^ ent; // xor hashing
if (htab[i] == fcode) {
ent = codetab[i];
continue;
} else if (htab[i] >= 0) // non-empty slot
{
disp = hsize_reg - i; // secondary hash (after G. Knott)
if (i == 0)
disp = 1;
do {
if ((i -= disp) < 0)
i += hsize_reg;
if (htab[i] == fcode) {
ent = codetab[i];
continue outer_loop;
}
} while (htab[i] >= 0);
}
output(ent, outs);
ent = c;
if (free_ent < maxmaxcode) {
codetab[i] = free_ent++; // code -> hashtable
htab[i] = fcode;
} else
cl_block(outs);
}
// Put out the final code.
output(ent, outs);
output(EOFCode, outs);
}
// ----------------------------------------------------------------------------
void encode(OutputStream os) throws IOException {
os.write(initCodeSize); // write "initial code size" byte
remaining = imgW * imgH; // reset navigation variables
curPixel = 0;
compress(initCodeSize + 1, os); // compress and write the pixel data
os.write(0); // write block terminator
}
// Flush the packet to disk, and reset the accumulator
void flush_char(OutputStream outs) throws IOException {
if (a_count > 0) {
outs.write(a_count);
outs.write(accum, 0, a_count);
a_count = 0;
}
}
final int MAXCODE(int n_bits) {
return (1 << n_bits) - 1;
}
// ----------------------------------------------------------------------------
// Return the next pixel from the image
// ----------------------------------------------------------------------------
private int nextPixel() {
if (remaining == 0)
return EOF;
--remaining;
byte pix = pixAry[curPixel++];
return pix & 0xff;
}
void output(int code, OutputStream outs) throws IOException {
cur_accum &= masks[cur_bits];
if (cur_bits > 0)
cur_accum |= (code << cur_bits);
else
cur_accum = code;
cur_bits += n_bits;
while (cur_bits >= 8) {
char_out((byte) (cur_accum & 0xff), outs);
cur_accum >>= 8;
cur_bits -= 8;
}
// If the next entry is going to be too big for the code size,
// then increase it, if possible.
if (free_ent > maxcode || clear_flg) {
if (clear_flg) {
maxcode = MAXCODE(n_bits = g_init_bits);
clear_flg = false;
} else {
++n_bits;
if (n_bits == maxbits)
maxcode = maxmaxcode;
else
maxcode = MAXCODE(n_bits);
}
}
if (code == EOFCode) {
// At EOF, write the rest of the buffer.
while (cur_bits > 0) {
char_out((byte) (cur_accum & 0xff), outs);
cur_accum >>= 8;
cur_bits -= 8;
}
flush_char(outs);
}
}
}
\ No newline at end of file
package com.mortals.xhx.base.login.web;
import com.mortals.framework.exception.AppException;
import com.mortals.framework.web.BaseForm;
public class LoginForm extends BaseForm {
private String loginName;
private String password;
private String securityCode;
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSecurityCode() {
return securityCode;
}
public void setSecurityCode(String securityCode) {
this.securityCode = securityCode;
}
@Override
public String toString() {
return "loginName:" + this.loginName + " password:" + this.password + " securityCode:" + this.securityCode;
}
@Override
public boolean validate() throws AppException {
if (loginName == null || loginName.trim().length() == 0) {
throw new AppException("帐号不能为空!");
}
if (password == null || password.trim().length() == 0) {
throw new AppException("密码不能为空!");
}
if (securityCode == null || securityCode.trim().length() == 0) {
throw new AppException("验证码不能为空!");
}
return super.validate();
}
}
package com.mortals.xhx.base.login.web;
import java.awt.image.BufferedImage;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.mortals.xhx.base.system.valid.model.ValidCodeEntity;
import com.mortals.xhx.base.system.valid.service.ValidCodeService;
import com.mortals.xhx.base.system.valid.web.ValidCodeForm;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.mortals.framework.util.SecurityImage;
import com.mortals.framework.web.BaseCRUDJsonController;
import com.mortals.xhx.base.login.service.GifSecurityImage;
@RestController
@RequestMapping("securitycode")
public class SecurityCodeController
extends BaseCRUDJsonController<ValidCodeService, ValidCodeForm, ValidCodeEntity, Long> {
@Autowired
private ValidCodeService validCodeService;
@RequestMapping("createCode")
public void createCode(HttpServletRequest request, HttpServletResponse response) {
// 获取默认难度和长度的验证码
String securityCode = validCodeService.createImageValidCode(request.getSession().getId(),
super.getRequestIP(request));
// int imageType = ParamUtil.getInt("securityImage", 1);
int imageType = 0;
switch (imageType) {
case 1:
BufferedImage image = SecurityImage.createImage(securityCode);
super.doResponseImage(response, image);
break;
default:
// 默认图验
byte[] content = GifSecurityImage.createGifImage(securityCode);
doResponseGif(response, content);
break;
}
// // 获取默认难度和长度的验证码
// String securityCode =
// validCodeService.createImageValidCode(request.getSession().getId(),
// super.getRequestIP(request));
// BufferedImage image = SecurityImage.createImage(securityCode);
// super.doResponseImage(response, image);
}
protected void doResponseGif(HttpServletResponse response, byte[] content) {
try {
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "No-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/gif");
IOUtils.write(content, response.getOutputStream());
} catch (Exception e) {
log.error("验证码图片传输异常-->" + e.getMessage());
}
}
}
package com.mortals.xhx.base.system.idgenerator.dao;
import com.mortals.framework.dao.ICRUDDao;
import com.mortals.xhx.base.system.idgenerator.model.IdgeneratorEntity;
/**
* <p>Title: id生成器</p>
* <p>Description: IdgeneratorDao DAO接口 </p>
* <p>Copyright: Copyright &reg; </p>
* <p>Company: </p>
*
* @author
* @version 1.0.0
*/
public interface IdgeneratorDao extends ICRUDDao<IdgeneratorEntity, Long> {
}
\ No newline at end of file
package com.mortals.xhx.base.system.idgenerator.dao.ibatis;
import com.mortals.framework.dao.ibatis.BaseCRUDDaoMybatis;
import com.mortals.xhx.base.system.idgenerator.dao.IdgeneratorDao;
import com.mortals.xhx.base.system.idgenerator.model.IdgeneratorEntity;
import org.springframework.stereotype.Repository;
/**
* <p>Title: id生成器</p>
* <p>Description: IdgeneratorDaoImpl DAO接口 </p>
* <p>Copyright: Copyright &reg; </p>
* <p>Company: </p>
*
* @author
* @version 1.0.0
*/
@Repository("idgeneratorDao")
public class IdgeneratorDaoImpl extends BaseCRUDDaoMybatis<IdgeneratorEntity, Long> implements IdgeneratorDao {
}
\ No newline at end of file
package com.mortals.xhx.base.system.idgenerator.model;
import com.mortals.framework.model.BaseEntityLong;
import java.util.Date;
/**
* <p>Title: id生成器</p>
* <p>Description: IdgeneratorEntity </p>
* <p>Copyright: Copyright &reg; </p>
* <p>Company: </p>
*
* @author
* @version 1.0.0
*/
public class IdgeneratorEntity extends BaseEntityLong {
private static final long serialVersionUID = 1555564885341L;
/** id类型 */
private String idType;
/** 当前id最大值 */
private Long idMaxValue;
/** 备注 */
private String remark;
/** 版本号,默认0 */
private Long versionNum;
/** 最后修改时间 */
private Date gmtModify;
public IdgeneratorEntity() {
}
/**
* 获取 id类型
*
* @return idType
*/
public String getIdType() {
return this.idType;
}
/**
* 设置 id类型
*
* @param idType
*/
public void setIdType(String idType) {
this.idType = idType;
}
/**
* 获取 当前id最大值
*
* @return idMaxValue
*/
public Long getIdMaxValue() {
return this.idMaxValue;
}
/**
* 设置 当前id最大值
*
* @param idMaxValue
*/
public void setIdMaxValue(Long idMaxValue) {
this.idMaxValue = idMaxValue;
}
/**
* 获取 备注
*
* @return remark
*/
public String getRemark() {
return this.remark;
}
/**
* 设置 备注
*
* @param remark
*/
public void setRemark(String remark) {
this.remark = remark;
}
/**
* 获取 版本号,默认0
*
* @return versionNum
*/
public Long getVersionNum() {
return this.versionNum;
}
/**
* 设置 版本号,默认0
*
* @param versionNum
*/
public void setVersionNum(Long versionNum) {
this.versionNum = versionNum;
}
/**
* 获取 最后修改时间
*
* @return gmtModify
*/
public Date getGmtModify() {
return this.gmtModify;
}
/**
* 设置 最后修改时间
*
* @param gmtModify
*/
public void setGmtModify(Date gmtModify) {
this.gmtModify = gmtModify;
}
@Override
public int hashCode() {
return this.getId().hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj instanceof IdgeneratorEntity) {
IdgeneratorEntity tmp = (IdgeneratorEntity) obj;
return this.getId().longValue() == tmp.getId().longValue();
}
return false;
}
@Override
public String toString() {
return "IdgeneratorEntity{" + "idType='" + idType + '\'' + ", idMaxValue=" + idMaxValue + ", remark='" + remark
+ '\'' + ", versionNum=" + versionNum + ", gmtModify=" + gmtModify + ", id=" + id + '}';
}
@Override
public void initAttrValue() {
this.idType = null;
this.idMaxValue = null;
this.remark = null;
this.versionNum = 0L;
this.gmtModify = null;
}
}
\ No newline at end of file
package com.mortals.xhx.base.system.idgenerator.model;
import java.util.List;
/**
* <p>Title: id生成器</p>
* <p>Description: IdgeneratorQuery </p>
* <p>Copyright: Copyright &reg; </p>
* <p>Company: </p>
*
* @author
* @version 1.0.0
*/
public class IdgeneratorQuery extends IdgeneratorEntity {
private static final long serialVersionUID = 1555564885342L;
/** 开始 菜单ID,主键,自增长 */
private Long idStart;
/** 结束 菜单ID,主键,自增长 */
private Long idEnd;
/** 增加 菜单ID,主键,自增长 */
private Long idIncrement;
/** 菜单ID,主键,自增长 */
private List<Long> idList;
/** id类型 */
private List<String> idTypeList;
/** 开始 当前id最大值 */
private Long idMaxValueStart;
/** 结束 当前id最大值 */
private Long idMaxValueEnd;
/** 增加 当前id最大值 */
private Long idMaxValueIncrement;
/** 当前id最大值 */
private List<Long> idMaxValueList;
/** 备注 */
private List<String> remarkList;
/** 开始 版本号,默认0 */
private Long versionNumStart;
/** 结束 版本号,默认0 */
private Long versionNumEnd;
/** 增加 版本号,默认0 */
private Long versionNumIncrement;
/** 版本号,默认0 */
private List<Long> versionNumList;
/** 开始 最后修改时间 */
private String gmtModifyStart;
/** 结束 最后修改时间 */
private String gmtModifyEnd;
public IdgeneratorQuery() {
}
/**
* 获取 开始 菜单ID,主键,自增长
*
* @return idStart
*/
public Long getIdStart() {
return this.idStart;
}
/**
* 设置 开始 菜单ID,主键,自增长
*
* @param idStart
*/
public void setIdStart(Long idStart) {
this.idStart = idStart;
}
/**
* 获取 结束 菜单ID,主键,自增长
*
* @return idEnd
*/
public Long getIdEnd() {
return this.idEnd;
}
/**
* 设置 结束 菜单ID,主键,自增长
*
* @param idEnd
*/
public void setIdEnd(Long idEnd) {
this.idEnd = idEnd;
}
/**
* 获取 增加 菜单ID,主键,自增长
*
* @return idIncrement
*/
public Long getIdIncrement() {
return this.idIncrement;
}
/**
* 设置 增加 菜单ID,主键,自增长
*
* @param idIncrement
*/
public void setIdIncrement(Long idIncrement) {
this.idIncrement = idIncrement;
}
/**
* 获取 菜单ID,主键,自增长
*
* @return idList
*/
public List<Long> getIdList() {
return this.idList;
}
/**
* 设置 菜单ID,主键,自增长
*
* @param idList
*/
public void setIdList(List<Long> idList) {
this.idList = idList;
}
/**
* 获取 id类型
*
* @return idTypeList
*/
public List<String> getIdTypeList() {
return this.idTypeList;
}
/**
* 设置 id类型
*
* @param idTypeList
*/
public void setIdTypeList(List<String> idTypeList) {
this.idTypeList = idTypeList;
}
/**
* 获取 开始 当前id最大值
*
* @return idMaxValueStart
*/
public Long getIdMaxValueStart() {
return this.idMaxValueStart;
}
/**
* 设置 开始 当前id最大值
*
* @param idMaxValueStart
*/
public void setIdMaxValueStart(Long idMaxValueStart) {
this.idMaxValueStart = idMaxValueStart;
}
/**
* 获取 结束 当前id最大值
*
* @return idMaxValueEnd
*/
public Long getIdMaxValueEnd() {
return this.idMaxValueEnd;
}
/**
* 设置 结束 当前id最大值
*
* @param idMaxValueEnd
*/
public void setIdMaxValueEnd(Long idMaxValueEnd) {
this.idMaxValueEnd = idMaxValueEnd;
}
/**
* 获取 增加 当前id最大值
*
* @return idMaxValueIncrement
*/
public Long getIdMaxValueIncrement() {
return this.idMaxValueIncrement;
}
/**
* 设置 增加 当前id最大值
*
* @param idMaxValueIncrement
*/
public void setIdMaxValueIncrement(Long idMaxValueIncrement) {
this.idMaxValueIncrement = idMaxValueIncrement;
}
/**
* 获取 当前id最大值
*
* @return idMaxValueList
*/
public List<Long> getIdMaxValueList() {
return this.idMaxValueList;
}
/**
* 设置 当前id最大值
*
* @param idMaxValueList
*/
public void setIdMaxValueList(List<Long> idMaxValueList) {
this.idMaxValueList = idMaxValueList;
}
/**
* 获取 备注
*
* @return remarkList
*/
public List<String> getRemarkList() {
return this.remarkList;
}
/**
* 设置 备注
*
* @param remarkList
*/
public void setRemarkList(List<String> remarkList) {
this.remarkList = remarkList;
}
/**
* 获取 开始 版本号,默认0
*
* @return versionNumStart
*/
public Long getVersionNumStart() {
return this.versionNumStart;
}
/**
* 设置 开始 版本号,默认0
*
* @param versionNumStart
*/
public void setVersionNumStart(Long versionNumStart) {
this.versionNumStart = versionNumStart;
}
/**
* 获取 结束 版本号,默认0
*
* @return versionNumEnd
*/
public Long getVersionNumEnd() {
return this.versionNumEnd;
}
/**
* 设置 结束 版本号,默认0
*
* @param versionNumEnd
*/
public void setVersionNumEnd(Long versionNumEnd) {
this.versionNumEnd = versionNumEnd;
}
/**
* 获取 增加 版本号,默认0
*
* @return versionNumIncrement
*/
public Long getVersionNumIncrement() {
return this.versionNumIncrement;
}
/**
* 设置 增加 版本号,默认0
*
* @param versionNumIncrement
*/
public void setVersionNumIncrement(Long versionNumIncrement) {
this.versionNumIncrement = versionNumIncrement;
}
/**
* 获取 版本号,默认0
*
* @return versionNumList
*/
public List<Long> getVersionNumList() {
return this.versionNumList;
}
/**
* 设置 版本号,默认0
*
* @param versionNumList
*/
public void setVersionNumList(List<Long> versionNumList) {
this.versionNumList = versionNumList;
}
/**
* 获取 开始 最后修改时间
*
* @return gmtModifyStart
*/
public String getGmtModifyStart() {
return this.gmtModifyStart;
}
/**
* 设置 开始 最后修改时间
*
* @param gmtModifyStart
*/
public void setGmtModifyStart(String gmtModifyStart) {
this.gmtModifyStart = gmtModifyStart;
}
/**
* 获取 结束 最后修改时间
*
* @return gmtModifyEnd
*/
public String getGmtModifyEnd() {
return this.gmtModifyEnd;
}
/**
* 设置 结束 最后修改时间
*
* @param gmtModifyEnd
*/
public void setGmtModifyEnd(String gmtModifyEnd) {
this.gmtModifyEnd = gmtModifyEnd;
}
}
\ No newline at end of file
package com.mortals.xhx.base.system.idgenerator.service;
import java.util.List;
import com.mortals.xhx.base.system.idgenerator.service.impl.IdgeneratorServiceImpl.IdGeneratorKey;
/**
* <p>Title: id生成器</p>
* <p>Description: IdgeneratorService service接口 </p>
* <p>Copyright: Copyright &reg; </p>
* <p>Company: </p>
*
* @author
* @version 1.0.0
*/
public interface IdgeneratorService {
/**
* 获取Long类型ID
*
* @param idGeneratorKey
* @param factorInfo 生成因子,根据各IdGeneratorKey要求选择传入
* @return
* @createTime 2016年3月16日 上午10:43:22
*/
Long getLongId(IdGeneratorKey idGeneratorKey, Object... factorInfo);
/**
* 获取String类型ID
*
* @param idGeneratorKey
* @param factorInfo 生成因子,根据各IdGeneratorKey要求选择传入
* @return
* @createTime 2016年3月16日 上午10:43:38
*/
String getStringId(IdGeneratorKey idGeneratorKey, Object... factorInfo);
/**
* 批量获取Long类型ID
*
* @param idGeneratorKey
* @param factorInfo
* @return
*/
List<Long> getLongIdList(IdGeneratorKey idGeneratorKey, Object... factorInfo);
}
\ No newline at end of file
/**
* 文件:MenuDao.java
* 版本:1.0.0
* 日期:
* Copyright &reg;
* All right reserved.
*/
package com.mortals.xhx.base.system.menu.dao;
import com.mortals.framework.dao.ICRUDDao;
import com.mortals.xhx.base.system.menu.model.MenuEntity;
/**
* <p>Title: 菜单信息</p>
* <p>Description: MenuDao DAO接口 </p>
* <p>Copyright: Copyright &reg; </p>
* <p>Company: </p>
* @author
* @version 1.0.0
*/
public interface MenuDao extends ICRUDDao<MenuEntity,Long> {
}
\ No newline at end of file
/**
* 文件:MenuDaoImpl.java
* 版本:1.0.0
* 日期:
* Copyright &reg;
* All right reserved.
*/
package com.mortals.xhx.base.system.menu.dao.ibatis;
import com.mortals.framework.dao.ibatis.BaseCRUDDaoMybatis;
import com.mortals.xhx.base.system.menu.dao.MenuDao;
import com.mortals.xhx.base.system.menu.model.MenuEntity;
import org.springframework.stereotype.Repository;
/**
* <p>Title: 菜单信息</p>
* <p>Description: MenuDaoImpl DAO接口 </p>
* <p>Copyright: Copyright &reg; </p>
* <p>Company: </p>
* @author
* @version 1.0.0
*/
@Repository("menuDao")
public class MenuDaoImpl extends BaseCRUDDaoMybatis<MenuEntity,Long> implements MenuDao {
}
\ No newline at end of file
/**
* 文件:MenuEntity.java
* 版本:1.0.0
* 日期:
* Copyright &reg;
* All right reserved.
*/
package com.mortals.xhx.base.system.menu.model;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.mortals.framework.model.BaseEntityLong;
/**
* <p>Title: 菜单信息</p>
* <p>Description: MenuEntity </p>
* <p>Copyright: Copyright &reg; </p>
* <p>Company: </p>
* @author
* @version 1.0.0
*/
public class MenuEntity extends BaseEntityLong{
private static final long serialVersionUID = 1547777703328L;
/** 菜单名称 */
private String name;
/** 连接地址 */
private String url;
/** 父菜单ID,一级菜单的该字段值为-1 */
private Long parentId;
/** 排序编号 */
private Integer orderId;
/** 菜单状态,0:禁用,1:启用,默认1 */
private Integer status;
/** 链接方式,0:普通,1:弹出,2:脚本(JavaScript),默认0 */
private Integer linkType;
/** 分组编号,使用菜单分隔符按该值分隔,默认1 */
private Integer groupId;
/** 分组名称 */
private Integer groupName;
/** 主菜单图标,主菜单图标的css样式名 */
private String imgPath;
/** 按钮图标,按钮图标的css样式名 */
private String buttonImgPath;
/** 常用菜单图标,常用菜单图标的css样式名 */
private String imgCommPath;
/** 是否常用菜单,0:非常用,1:常用,默认0 */
private Integer commMenu;
/** 菜单类型,0:主菜单,1:非主菜单(功能操作组),默认0 */
private Integer menuType;
/** 权限类型,0:无限制,1:无需登录查看,2:需要登录查看,3:需要角色权限查看,默认3 */
private Integer authType;
/** 创建时间 */
private Date createTime;
/** 创建用户 */
private String createUser;
/** 创建用户名称 */
private String createUserName;
private List<MenuEntity> childList = new ArrayList<MenuEntity>();
public MenuEntity() {
}
/**
* 获取 菜单名称
* @return name
*/
public String getName() {
return this.name;
}
/**
* 设置 菜单名称
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取 连接地址
* @return url
*/
public String getUrl() {
return this.url;
}
/**
* 设置 连接地址
* @param url
*/
public void setUrl(String url) {
this.url = url;
}
/**
* 获取 父菜单ID,一级菜单的该字段值为-1
* @return parentId
*/
public Long getParentId() {
return this.parentId;
}
/**
* 设置 父菜单ID,一级菜单的该字段值为-1
* @param parentId
*/
public void setParentId(Long parentId) {
this.parentId = parentId;
}
/**
* 获取 排序编号
* @return orderId
*/
public Integer getOrderId() {
return this.orderId;
}
/**
* 设置 排序编号
* @param orderId
*/
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
/**
* 获取 菜单状态,0:禁用,1:启用,默认1
* @return status
*/
public Integer getStatus() {
return this.status;
}
/**
* 设置 菜单状态,0:禁用,1:启用,默认1
* @param status
*/
public void setStatus(Integer status) {
this.status = status;
}
/**
* 获取 链接方式,0:普通,1:弹出,2:脚本(JavaScript),默认0
* @return linkType
*/
public Integer getLinkType() {
return this.linkType;
}
/**
* 设置 链接方式,0:普通,1:弹出,2:脚本(JavaScript),默认0
* @param linkType
*/
public void setLinkType(Integer linkType) {
this.linkType = linkType;
}
/**
* 获取 分组编号,使用菜单分隔符按该值分隔,默认1
* @return groupId
*/
public Integer getGroupId() {
return this.groupId;
}
/**
* 设置 分组编号,使用菜单分隔符按该值分隔,默认1
* @param groupId
*/
public void setGroupId(Integer groupId) {
this.groupId = groupId;
}
public Integer getGroupName() {
return groupName;
}
public void setGroupName(Integer groupName) {
this.groupName = groupName;
}
/**
* 获取 主菜单图标,主菜单图标的css样式名
* @return imgPath
*/
public String getImgPath() {
return this.imgPath;
}
/**
* 设置 主菜单图标,主菜单图标的css样式名
* @param imgPath
*/
public void setImgPath(String imgPath) {
this.imgPath = imgPath;
}
/**
* 获取 按钮图标,按钮图标的css样式名
* @return buttonImgPath
*/
public String getButtonImgPath() {
return this.buttonImgPath;
}
/**
* 设置 按钮图标,按钮图标的css样式名
* @param buttonImgPath
*/
public void setButtonImgPath(String buttonImgPath) {
this.buttonImgPath = buttonImgPath;
}
/**
* 获取 常用菜单图标,常用菜单图标的css样式名
* @return imgCommPath
*/
public String getImgCommPath() {
return this.imgCommPath;
}
/**
* 设置 常用菜单图标,常用菜单图标的css样式名
* @param imgCommPath
*/
public void setImgCommPath(String imgCommPath) {
this.imgCommPath = imgCommPath;
}
/**
* 获取 是否常用菜单,0:非常用,1:常用,默认0
* @return commMenu
*/
public Integer getCommMenu() {
return this.commMenu;
}
/**
* 设置 是否常用菜单,0:非常用,1:常用,默认0
* @param commMenu
*/
public void setCommMenu(Integer commMenu) {
this.commMenu = commMenu;
}
/**
* 获取 菜单类型,0:主菜单,1:非主菜单(功能操作组),默认0
* @return menuType
*/
public Integer getMenuType() {
return this.menuType;
}
/**
* 设置 菜单类型,0:主菜单,1:非主菜单(功能操作组),默认0
* @param menuType
*/
public void setMenuType(Integer menuType) {
this.menuType = menuType;
}
/**
* 获取 权限类型,0:无限制,1:无需登录查看,2:需要登录查看,3:需要角色权限查看,默认3
* @return authType
*/
public Integer getAuthType() {
return this.authType;
}
/**
* 设置 权限类型,0:无限制,1:无需登录查看,2:需要登录查看,3:需要角色权限查看,默认3
* @param authType
*/
public void setAuthType(Integer authType) {
this.authType = authType;
}
/**
* 获取 创建时间
* @return createTime
*/
@Override
public Date getCreateTime() {
return this.createTime;
}
/**
* 设置 创建时间
* @param createTime
*/
@Override
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
/**
* 获取 创建用户
* @return createUser
*/
@Override
public String getCreateUser() {
return this.createUser;
}
/**
* 设置 创建用户
* @param createUser
*/
@Override
public void setCreateUser(String createUser) {
this.createUser = createUser;
}
/**
* 获取 创建用户名称
* @return createUserName
*/
@Override
public String getCreateUserName() {
return this.createUserName;
}
/**
* 设置 创建用户名称
* @param createUserName
*/
@Override
public void setCreateUserName(String createUserName) {
this.createUserName = createUserName;
}
public List<MenuEntity> getChildList() {
return childList;
}
public void setChildList(List<MenuEntity> childList) {
this.childList = childList;
}
@Override
public int hashCode() {
return this.getId().hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj instanceof MenuEntity) {
MenuEntity tmp = (MenuEntity) obj;
if (this.getId().longValue() == tmp.getId().longValue()) {
return true;
}
}
return false;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("");
sb.append("id:").append(getId())
.append(",name:").append(getName())
.append(",url:").append(getUrl())
.append(",parentId:").append(getParentId())
.append(",orderId:").append(getOrderId())
.append(",status:").append(getStatus())
.append(",linkType:").append(getLinkType())
.append(",groupId:").append(getGroupId())
.append(",groupName:").append(getGroupName())
.append(",imgPath:").append(getImgPath())
.append(",buttonImgPath:").append(getButtonImgPath())
.append(",imgCommPath:").append(getImgCommPath())
.append(",commMenu:").append(getCommMenu())
.append(",menuType:").append(getMenuType())
.append(",authType:").append(getAuthType())
.append(",createTime:").append(getCreateTime())
.append(",createUser:").append(getCreateUser())
.append(",createUserName:").append(getCreateUserName());
return sb.toString();
}
@Override
public void initAttrValue() {
this.name = null;
this.url = null;
this.parentId = null;
this.orderId = null;
this.status = 1;
this.linkType = 0;
this.groupId = 1;
this.imgPath = null;
this.buttonImgPath = null;
this.imgCommPath = null;
this.commMenu = 0;
this.menuType = 0;
this.authType = 3;
this.createTime = null;
this.createUser = null;
this.createUserName = null;
}
}
\ No newline at end of file
/**
* 文件:MenuService.java
* 版本:1.0.0
* 日期:
* Copyright &reg;
* All right reserved.
*/
package com.mortals.xhx.base.system.menu.service;
import com.mortals.framework.exception.AppException;
import com.mortals.framework.service.ICRUDService;
import com.mortals.framework.service.IUser;
import com.mortals.xhx.base.system.menu.model.MenuEntity;
import java.util.List;
import java.util.Set;
/**
* <p>Title: 菜单信息</p>
* <p>Description: MenuService service接口 </p>
* <p>Copyright: Copyright &reg; </p>
* <p>Company: </p>
* @author
* @version 1.0.0
*/
public interface MenuService extends ICRUDService<MenuEntity,Long> {
/**
* 获取所有可用菜单
*
* @return
* @throws AppException
*/
public List<MenuEntity> findAllEnable() throws AppException;
/**
* 查询有权限的菜单
*
* @param user 当前用户
* @param urls 有权限的访问地址集合
* @return
* @throws AppException
*/
public List<MenuEntity> findTreeMenu(IUser user, Set<String> urls) throws AppException;
/**
* 查看所有菜单
*
* @return
* @throws AppException
*/
public List<MenuEntity> findTreeMenu() throws AppException;
/**
* 更新排列顺序
* @param id
* @param type
*/
void upOrDown(Long id, Integer type);
}
\ No newline at end of file
/**
* 文件:MenuServiceImpl.java
* 版本:1.0.0
* 日期:
* Copyright &reg;
* All right reserved.
*/
package com.mortals.xhx.base.system.menu.service.impl;
import com.mortals.framework.common.code.YesNo;
import com.mortals.framework.exception.AppException;
import com.mortals.framework.model.Context;
import com.mortals.framework.model.OrderCol;
import com.mortals.framework.service.IUser;
import com.mortals.framework.service.impl.AbstractCRUDServiceImpl;
import com.mortals.framework.util.StringUtils;
import com.mortals.xhx.base.system.menu.dao.MenuDao;
import com.mortals.xhx.base.system.menu.model.MenuEntity;
import com.mortals.xhx.base.system.menu.model.MenuQuery;
import com.mortals.xhx.base.system.menu.service.MenuService;
import com.mortals.xhx.common.code.YesNoEnum;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
/**
* <p>Title: 菜单信息</p>
* <p>Description: MenuServiceImpl service接口 </p>
* <p>Copyright: Copyright &reg; </p>
* <p>Company: </p>
*
* @author
* @version 1.0.0
*/
@Service("menuService")
public class MenuServiceImpl extends AbstractCRUDServiceImpl<MenuDao, MenuEntity, Long> implements MenuService {
@Override
public List<MenuEntity> findAllEnable() throws AppException {
MenuQuery params = new MenuQuery();
params.setStatus(YesNo.YES.getValue());
params.setMenuType(0);//主菜单
List<OrderCol> orderColList = new ArrayList<OrderCol>();
orderColList.add(new OrderCol("parentId"));
orderColList.add(new OrderCol("orderId"));
params.setOrderColList(orderColList);
return dao.getList(params);
}
@Override
public List<MenuEntity> findTreeMenu(IUser user, Set<String> urls) throws AppException {
Set<Long> authIds = new HashSet<Long>();
Map<Long, MenuEntity> menuMap = new HashMap<Long, MenuEntity>();
List<MenuEntity> userModuleList = this.findAllEnable();
for (MenuEntity sysModule : userModuleList) {
if (sysModule == null) {
continue;
}
menuMap.put(sysModule.getId(), sysModule);
if (!user.isAdmin() && urls.contains(StringUtils.trim(sysModule.getUrl()))) {
authIds.add(sysModule.getId());
}
}
if (!user.isAdmin()) {
Long[] ids = authIds.toArray(new Long[authIds.size()]);
for (Long id : ids) {
MenuEntity menu = menuMap.get(id);
while (menu != null) {
authIds.add(menu.getId());
menu = menuMap.get(menu.getParentId());
}
}
}
List<MenuEntity> outlookBar = new ArrayList<MenuEntity>();
List<MenuEntity> norightList = new ArrayList<MenuEntity>();
for (MenuEntity sysModule : userModuleList) {
if (!user.isAdmin() && !authIds.contains(sysModule.getId())) {
norightList.add(sysModule);
continue;
}
if (sysModule.getParentId() == null || sysModule.getParentId().longValue() == 0) {
outlookBar.add(sysModule);
continue;
}
MenuEntity sysModuleParent = menuMap.get(sysModule.getParentId());
if (sysModuleParent != null) {
sysModuleParent.getChildList().add(sysModule);
}
}
return outlookBar;
}
public List<MenuEntity> findTreeMenu() throws AppException {
Map<Long, MenuEntity> menuMap = new HashMap<Long, MenuEntity>();
List<MenuEntity> userModuleList = this.findAllEnable();
for (MenuEntity sysModule : userModuleList) {
if (sysModule == null) {
continue;
}
menuMap.put(sysModule.getId(), sysModule);
}
List<MenuEntity> outlookBar = new ArrayList<MenuEntity>();
for (MenuEntity sysModule : userModuleList) {
if (sysModule.getParentId() == null || sysModule.getParentId().longValue() == 0) {
outlookBar.add(sysModule);
continue;
}
MenuEntity sysModuleParent = menuMap.get(sysModule.getParentId());
if (sysModuleParent != null) {
sysModuleParent.getChildList().add(sysModule);
}
}
return outlookBar;
}
@Override
public void upOrDown(Long id, Integer type) {
MenuQuery query = new MenuQuery();
query.setOrderColList(Arrays.asList(new OrderCol("orderId")));
query.setOrderKind(OrderCol.ASCENDING);
//判断移动的是一级菜单还是二级菜单
MenuEntity menuEntity = this.get(id);
if (menuEntity.getParentId() == 0) {
//一级菜单
query.setParentId(0L);
} else {
//二级菜单
query.setParentId(menuEntity.getParentId());
}
List<MenuEntity> list = this.find(query);
MenuEntity preEntity = null;
MenuEntity curEntity = null;
if (type == YesNoEnum.NO.getValue()) {
//up
for (int i = 0; i < list.size(); i++) {
if (list.get(i).getId().equals(id) && i > 0) {
curEntity = list.get(i);
preEntity = list.get(i - 1);
break;
}
}
} else {
//down
for (int i = 0; i < list.size(); i++) {
if (list.get(i).getId().equals(id) && i + 1 < list.size()) {
curEntity = list.get(i);
preEntity = list.get(i + 1);
break;
}
}
}
if (preEntity != null && curEntity != null) {
//交换
int temp = preEntity.getOrderId();
preEntity.setOrderId(curEntity.getOrderId());
curEntity.setOrderId(temp);
this.update(preEntity);
this.update(curEntity);
}
}
@Override
protected void saveBefore(MenuEntity entity, Context context) throws AppException {
MenuQuery query = new MenuQuery();
query.setParentId(entity.getParentId());
Comparator<Integer> comparator = Comparator.comparing(Integer::intValue);
Optional<Integer> maxOptional = this.find(query).stream().map(MenuEntity::getOrderId).filter(f->f!=9999).max(comparator);
maxOptional.ifPresent(e -> {
e=e+1;
entity.setOrderId(e);
});
super.saveBefore(entity, context);
}
}
\ No newline at end of file
package com.mortals.xhx.base.system.menu.web;
import com.alibaba.fastjson.JSONObject;
import com.mortals.framework.exception.AppException;
import com.mortals.framework.model.Context;
import com.mortals.framework.model.OrderCol;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.mortals.framework.web.BaseCRUDJsonMappingController;
import com.mortals.xhx.base.system.menu.model.MenuEntity;
import com.mortals.xhx.base.system.menu.service.MenuService;
import com.mortals.xhx.common.code.DataSatus;
import com.mortals.xhx.common.code.MenuAuthType;
import com.mortals.xhx.common.code.MenuComm;
import com.mortals.xhx.common.code.MenuLinkType;
import com.mortals.xhx.common.code.MenuType;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 菜单信息
*
* @author: zxfei
* @date: 2021/11/30 10:02
*/
@RestController
@RequestMapping("menu")
public class MenuController extends BaseCRUDJsonMappingController<MenuService, MenuForm,MenuEntity,Long> {
public MenuController(){
super.setFormClass(MenuForm.class);
super.setModuleDesc("菜单信息");
}
@Override
protected void doListBefore(HttpServletRequest request, HttpServletResponse response, MenuForm form, Map<String, Object> model, Context context) throws AppException {
List<OrderCol> orderColList = new ArrayList<OrderCol>();
orderColList.add(new OrderCol("parentId"));
orderColList.add(new OrderCol("orderId"));
form.getQuery().setOrderColList(orderColList);
}
@Override
protected void init(HttpServletRequest request, HttpServletResponse response, MenuForm form, Map<String, Object> model,
Context context) {
Map<String, Object> status = new HashMap<String, Object>();
status.put("status", DataSatus.getEnumMap(DataSatus.CLOSE.getValue(), DataSatus.DELETE.getValue(), DataSatus.OVERDUE.getValue(), DataSatus.USEOUT.getValue()));
status.put("linkType", MenuLinkType.getEnumMap());
status.put("commMenu", MenuComm.getEnumMap());
status.put("menuType", MenuType.getEnumMap());
status.put("authType", MenuAuthType.getEnumMap());
model.put(KEY_RESULT_DICT, status);
super.init(request, response, form, model, context);
}
/**
* 改变状态
*/
@RequestMapping(value = "change/status")
public String changeStatus(HttpServletRequest request, HttpServletResponse response, MenuForm form) {
JSONObject ret = new JSONObject();
Context context = getContext();
try {
MenuEntity entity = this.service.get(form.getEntity().getId(), context);//.doSubmitAudit(form.getEntity(), context);
if (null == entity) {
throw new AppException("菜单不存在!");
}
if (null == form.getEntity().getStatus()) {
throw new AppException("菜单状态不能为空!");
}
if (form.getEntity().getStatus() != DataSatus.ENABLE.getValue() && form.getEntity().getStatus() != DataSatus.DISENABLE.getValue()) {
throw new AppException("非法菜单状态!");
}
String busiDesc = DataSatus.getByValue(form.getEntity().getStatus()).getDesc();
entity.setStatus(form.getEntity().getStatus());
this.service.update(entity, context);
ret.put(KEY_RESULT_CODE, VALUE_RESULT_SUCCESS);
ret.put(KEY_RESULT_MSG, busiDesc + "成功");
recordSysLog(request, busiDesc + " 【成功】 [id:" + entity.getId() + "]");
} catch (Exception e) {
ret.put(KEY_RESULT_CODE, VALUE_RESULT_FAILURE);
ret.put(KEY_RESULT_MSG, super.convertException(e));
}
return ret.toJSONString();
}
/**
* 更换排序
*/
@PostMapping(value = "upOrDown")
public String upOrDownTopicList(@RequestParam(value = "id") Long id, @RequestParam(value = "type") Integer type) {
JSONObject ret = new JSONObject();
ret.put(KEY_RESULT_CODE, VALUE_RESULT_SUCCESS);
try {
this.service.upOrDown(id,type);
} catch (Exception e) {
log.error("更新错误", e);
ret.put(KEY_RESULT_CODE, VALUE_RESULT_FAILURE);
ret.put(KEY_RESULT_MSG, e.getMessage());
}
return ret.toJSONString();
}
}
\ No newline at end of file
/**
* 文件:MenuForm.java
* 版本:1.0.0
* 日期:
* Copyright &reg;
* All right reserved.
*/
package com.mortals.xhx.base.system.menu.web;
import com.mortals.framework.web.BaseCRUDFormLong;
import com.mortals.xhx.base.system.menu.model.MenuEntity;
import com.mortals.xhx.base.system.menu.model.MenuQuery;
/**
* <p>Title: 菜单信息</p>
* <p>Description: MenuForm </p>
* <p>Copyright: Copyright &reg; </p>
* <p>Company: </p>
* @author
* @version 1.0.0
*/
public class MenuForm extends BaseCRUDFormLong<MenuEntity> {
private MenuEntity entity = new MenuEntity();
private MenuQuery query = new MenuQuery();
public MenuForm(){
}
@Override
public MenuEntity getEntity() {
return entity;
}
public void setEntity(MenuEntity entity) {
this.entity = entity;
}
@Override
public MenuQuery getQuery() {
return query;
}
public void setQuery(MenuQuery query) {
this.query = query;
}
}
\ No newline at end of file
/**
* 文件:OperLogDao.java
* 版本:1.0.0
* 日期:
* Copyright &reg;
* All right reserved.
*/
package com.mortals.xhx.base.system.oper.dao;
import com.mortals.framework.dao.ICRUDDao;
import com.mortals.xhx.base.system.oper.model.OperLogEntity;
/**
* <p>Title: 操作日志</p>
* <p>Description: OperLogDao DAO接口 </p>
* <p>Copyright: Copyright &reg; </p>
* <p>Company: </p>
* @author
* @version 1.0.0
*/
public interface OperLogDao extends ICRUDDao<OperLogEntity,Long> {
}
\ No newline at end of file
/**
* 文件:OperLogDaoImpl.java
* 版本:1.0.0
* 日期:
* Copyright &reg;
* All right reserved.
*/
package com.mortals.xhx.base.system.oper.dao.ibatis;
import com.mortals.framework.dao.ibatis.BaseCRUDDaoMybatis;
import com.mortals.xhx.base.system.oper.dao.OperLogDao;
import com.mortals.xhx.base.system.oper.model.OperLogEntity;
import org.springframework.stereotype.Repository;
/**
* <p>Title: 操作日志</p>
* <p>Description: OperLogDaoImpl DAO接口 </p>
* <p>Copyright: Copyright &reg; </p>
* <p>Company: </p>
* @author
* @version 1.0.0
*/
@Repository("operLogDao")
public class OperLogDaoImpl extends BaseCRUDDaoMybatis<OperLogEntity,Long> implements OperLogDao {
}
\ No newline at end of file
/**
* 文件:OperLogService.java
* 版本:1.0.0
* 日期:
* Copyright &reg;
* All right reserved.
*/
package com.mortals.xhx.base.system.oper.service;
import com.mortals.framework.model.BaseEntity;
import com.mortals.framework.service.ICRUDService;
import com.mortals.framework.service.IUser;
import com.mortals.xhx.base.system.oper.model.OperLogEntity;
import com.mortals.xhx.common.code.OperTypeEnum;
import javax.servlet.http.HttpServletRequest;
/**
* <p>Title: 操作日志</p>
* <p>Description: OperLogService service接口 </p>
* <p>Copyright: Copyright &reg; </p>
* <p>Company: </p>
* @author
* @version 1.0.0
*/
public interface OperLogService extends ICRUDService<OperLogEntity,Long> {
/**
* 保存新增数据的日志信息
* @param request 发送请求信息
* @param user 当前登录用户
* @param result 新增后的返回数据
*/
void saveCreateOperLog(HttpServletRequest request, IUser user, String result);
/**
* 保存更新数据日志信息
* @param request 发送请求信息
* @param user 当前登录用户
* @param oldEntity 原始数据
* @param newEntity 新数据
*/
void saveUpdateOperLog(HttpServletRequest request, IUser user, BaseEntity oldEntity, BaseEntity newEntity);
/**
* 保存删除操作日志信息
* @param request 发送请求信息
* @param user 当前登录用户
* @param result 新增后的返回数据
* @param id 删除的数据id
*/
void saveDeleteOperLog(HttpServletRequest request, IUser user, String result, String id);
/**
* 保存新增信息操作日志,内容直接取content字段,如果存在id会加上id信息
* @param request 发送请求信息
* @param user 当前登录用户
* @param content 需要记录的日志信息
* @param id 数据的id值
*/
void saveOperLog(HttpServletRequest request, IUser user, String content, String id);
/**
*
* @param request 发送请求信息
* @param user 当前登录用户
* @param oldEntity 原始数据
* @param newEntity 新数据
* @param operType 日志类型
* @param content 需要记录的日志信息
* @param id 数据的id值
*/
void insertOperLog(HttpServletRequest request, IUser user, BaseEntity oldEntity, BaseEntity newEntity, OperTypeEnum operType, String content, String id);
void insertOperLog(String ip, String requestUrl, Long userId, String userName, String loginName, String content);
}
\ No newline at end of file
/**
* 文件:ParamDao.java
* 版本:1.0.0
* 日期:
* Copyright &reg;
* All right reserved.
*/
package com.mortals.xhx.base.system.param.dao;
import com.mortals.framework.dao.ICRUDDao;
import com.mortals.xhx.base.system.param.model.ParamEntity;
/**
* <p>Title: 参数信息</p>
* <p>Description: ParamDao DAO接口 </p>
* <p>Copyright: Copyright &reg; </p>
* <p>Company: </p>
* @author
* @version 1.0.0
*/
public interface ParamDao extends ICRUDDao<ParamEntity,Long> {
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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