Commit f326c44c authored by 雍欢's avatar 雍欢

系统参数缓存、初始化逻辑修改

parent 2ab637b7
package com.huigou.cache; package com.huigou.cache;
import java.io.Serializable; import java.io.Serializable;
import java.util.Arrays; import java.util.*;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.huigou.cache.service.ICache; import com.huigou.cache.service.ICache;
import com.huigou.util.ApplicationContextWrapper;
import com.huigou.util.ClassHelper; import com.huigou.util.ClassHelper;
import com.huigou.util.StringUtil; import com.huigou.util.StringUtil;
/** /**
* 单态模式保存系统常量使用ehcache缓存数据 * 单态模式保存系统常量使用ehcache缓存数据
* *
* @ClassName: Singleton
* @author xx * @author xx
* @version V1.0 * @version V1.0
* @ClassName: Singleton
*/ */
public class SystemCache { public class SystemCache {
/**
* @deprecated 从1.2.11 开始自动从spring容器中获取缓存管理器。
*/
@Deprecated
private ICache icache; private ICache icache;
private String startTime;// 服务启动时间 private String startTime;// 服务启动时间
...@@ -62,26 +64,32 @@ public class SystemCache { ...@@ -62,26 +64,32 @@ public class SystemCache {
getInstance().startTime = startTime; getInstance().startTime = startTime;
} }
@Deprecated
public static void setCache(ICache icache) { public static void setCache(ICache icache) {
getInstance().icache = icache; getInstance().icache = icache;
} }
private Optional<ICache> getIcache() {
ICache iCache = this.icache != null ? this.icache : (ICache) ApplicationContextWrapper.getBean("sysDataCache");
return Optional.ofNullable(iCache);
}
private void put(String cacheKey, Serializable obj) { private void put(String cacheKey, Serializable obj) {
icache.put(cacheKey, obj); getIcache().ifPresent(icache -> icache.put(cacheKey, obj));
} }
private Object get(String cacheKey) { private Object get(String cacheKey) {
if (icache == null) return null; return getIcache().map(icache -> icache.get(cacheKey))
return icache.get(cacheKey); .orElse(null);
} }
private <T> T get(String cacheKey, Class<T> cls) { private <T> T get(String cacheKey, Class<T> cls) {
if (icache == null) return null; return getIcache().map(icache -> icache.get(cacheKey, cls))
return icache.get(cacheKey, cls); .orElse(null);
} }
private void remove(String key) { private void remove(String key) {
icache.remove(key, "."); getIcache().ifPresent(icache -> icache.remove(key, "."));
} }
public static String getContextPath() { public static String getContextPath() {
......
package com.huigou.util; package com.huigou.util;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.Collection;
/** /**
* spring 环境包装类 * spring 环境包装类
* *
* @author xx * @author xx
*/ */
public class ApplicationContextWrapper { @Component
private ApplicationContext applicationContext; public class ApplicationContextWrapper implements ApplicationContextAware {
static class ApplicationContextHolder { private static ApplicationContext applicationContext;
static ApplicationContextWrapper instance = new ApplicationContextWrapper();
}
public static ApplicationContextWrapper getInstance() {
return ApplicationContextHolder.instance;
}
public void setApplicationContext(ApplicationContext context) { public void setApplicationContext(ApplicationContext context) {
this.applicationContext = context; ApplicationContextWrapper.applicationContext = context;
} }
public ApplicationContext getApplicationContext() { public ApplicationContext getApplicationContext() {
return applicationContext; return applicationContext;
} }
@Deprecated
public synchronized static void init(ApplicationContext context) { public synchronized static void init(ApplicationContext context) {
getInstance().setApplicationContext(context); if (context != null) {
ApplicationContextWrapper.applicationContext = context;
}
} }
public static ApplicationContext getContext() { public static ApplicationContext getContext() {
return getInstance().getApplicationContext(); return applicationContext;
} }
public static Object getBean(String beanName) { public static Object getBean(String beanName) {
if (getInstance().getApplicationContext() == null) { if (applicationContext == null) {
return null; return null;
} }
return getInstance().getApplicationContext().getBean(beanName); return applicationContext.getBean(beanName);
} }
/** /**
...@@ -49,19 +51,29 @@ public class ApplicationContextWrapper { ...@@ -49,19 +51,29 @@ public class ApplicationContextWrapper {
* @return * @return
*/ */
public static <T> T getBean(String name, Class<T> type) { public static <T> T getBean(String name, Class<T> type) {
if (getInstance().getApplicationContext() == null) { if (applicationContext == null) {
return null; return null;
} }
return getInstance().getApplicationContext().getBean(name, type); return applicationContext.getBean(name, type);
} }
/** /**
* @since 1.1.3 * @since 1.1.3
*/ */
public static <T> T getBean(Class<T> type) { public static <T> T getBean(Class<T> type) {
if (getInstance().getApplicationContext() == null) { if (applicationContext == null) {
return null;
}
return applicationContext.getBean(type);
}
/**
* @since 1.2.0
*/
public static <T> Collection<T> getBeans(Class<T> type) {
if (applicationContext == null) {
return null; return null;
} }
return getInstance().getApplicationContext().getBean(type); return applicationContext.getBeansOfType(type).values();
} }
} }
...@@ -184,7 +184,7 @@ public class SDO implements Serializable { ...@@ -184,7 +184,7 @@ public class SDO implements Serializable {
return this.getProperty(key, Integer.class); return this.getProperty(key, Integer.class);
} }
public Integer getInteger(String key,Integer defaultValue) { public Integer getInteger(String key, Integer defaultValue) {
return Optional.ofNullable(getInteger(key)).orElse(defaultValue); return Optional.ofNullable(getInteger(key)).orElse(defaultValue);
} }
...@@ -224,7 +224,7 @@ public class SDO implements Serializable { ...@@ -224,7 +224,7 @@ public class SDO implements Serializable {
return null; return null;
} }
@SuppressWarnings({ "unchecked" }) @SuppressWarnings({"unchecked"})
public <T> List<T> getList(String key, Class<T> clazz) { public <T> List<T> getList(String key, Class<T> clazz) {
List<T> beanList = new ArrayList<T>(); List<T> beanList = new ArrayList<T>();
List<Object> list = getList(key); List<Object> list = getList(key);
...@@ -261,6 +261,14 @@ public class SDO implements Serializable { ...@@ -261,6 +261,14 @@ public class SDO implements Serializable {
return null; return null;
} }
public <T> Optional<T> getObject(String key, Class<T> clazz) {
Object value = properties.get(key);
if (value == null) {
return Optional.empty();
}
return Optional.ofNullable((T) value);
}
public void parseJSONString(String jsonStr) { public void parseJSONString(String jsonStr) {
if (StringUtil.isBlank(jsonStr)) { if (StringUtil.isBlank(jsonStr)) {
return; return;
...@@ -276,10 +284,10 @@ public class SDO implements Serializable { ...@@ -276,10 +284,10 @@ public class SDO implements Serializable {
/** /**
* SDO中数据转换为对象 * SDO中数据转换为对象
* *
* @author
* @param cls * @param cls
* @return * @return
* @throws Exception * @throws Exception
* @author
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T toObject(Class<T> cls) { public <T> T toObject(Class<T> cls) {
...@@ -335,10 +343,9 @@ public class SDO implements Serializable { ...@@ -335,10 +343,9 @@ public class SDO implements Serializable {
/** /**
* 改变键名称 * 改变键名称
* *
* @author
* @param oldName * @param oldName
* @param newName * @param newName void
* void * @author
*/ */
public void changKeyName(String oldName, String newName) { public void changKeyName(String oldName, String newName) {
Object value = this.getProperty(oldName); Object value = this.getProperty(oldName);
......
...@@ -3,6 +3,7 @@ package com.huigou.uasp.bmp.opm.repository.org; ...@@ -3,6 +3,7 @@ package com.huigou.uasp.bmp.opm.repository.org;
import java.util.List; import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import com.huigou.uasp.bmp.opm.domain.model.org.Org; import com.huigou.uasp.bmp.opm.domain.model.org.Org;
...@@ -13,7 +14,7 @@ import com.huigou.uasp.bmp.opm.domain.model.org.OrgType; ...@@ -13,7 +14,7 @@ import com.huigou.uasp.bmp.opm.domain.model.org.OrgType;
* *
* @author gongmm * @author gongmm
*/ */
public interface OrgRepository extends JpaRepository<Org, String> { public interface OrgRepository extends JpaRepository<Org, String>, JpaSpecificationExecutor<Org> {
List<Org> findByFullIdLike(String fullId); List<Org> findByFullIdLike(String fullId);
......
...@@ -35,6 +35,7 @@ import com.huigou.util.*; ...@@ -35,6 +35,7 @@ import com.huigou.util.*;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert; import org.springframework.util.Assert;
...@@ -67,6 +68,10 @@ public class OrgApplicationImpl extends BaseApplication implements OrgApplicatio ...@@ -67,6 +68,10 @@ public class OrgApplicationImpl extends BaseApplication implements OrgApplicatio
private ManagementApplication managementApplication; private ManagementApplication managementApplication;
private LicenseChecker licenseChecker; private LicenseChecker licenseChecker;
/**
* 是否强制生成主键
*/
private boolean forceGenerateIdentifier = true;
@Autowired @Autowired
public void setOrgPropertyDefinitionRepository(OrgPropertyDefinitionRepository orgPropertyDefinitionRepository) { public void setOrgPropertyDefinitionRepository(OrgPropertyDefinitionRepository orgPropertyDefinitionRepository) {
...@@ -133,6 +138,12 @@ public class OrgApplicationImpl extends BaseApplication implements OrgApplicatio ...@@ -133,6 +138,12 @@ public class OrgApplicationImpl extends BaseApplication implements OrgApplicatio
return licenseChecker; return licenseChecker;
} }
@Autowired(required = false)
@Value("${org.forceGenerateIdentifier}")
public void setForceGenerateIdentifier(boolean forceGenerateIdentifier) {
this.forceGenerateIdentifier = forceGenerateIdentifier;
}
@Transactional(rollbackFor = RuntimeException.class) @Transactional(rollbackFor = RuntimeException.class)
@Override @Override
public String saveOrgPropertyDefinition(OrgPropertyDefinition orgPropertyDefinition) { public String saveOrgPropertyDefinition(OrgPropertyDefinition orgPropertyDefinition) {
...@@ -477,8 +488,10 @@ public class OrgApplicationImpl extends BaseApplication implements OrgApplicatio ...@@ -477,8 +488,10 @@ public class OrgApplicationImpl extends BaseApplication implements OrgApplicatio
parent = orgRepository.findOne(org.getParentId()); parent = orgRepository.findOne(org.getParentId());
} }
if (StringUtils.isBlank(org.getId()) || StringUtils.isNotBlank(org.getId()) && forceGenerateIdentifier) {
String id = CommonUtil.createGUID(); String id = CommonUtil.createGUID();
org.setId(id); org.setId(id);
}
if (StringUtil.isBlank(org.getTenantId())) { if (StringUtil.isBlank(org.getTenantId())) {
setOrgTenantId(org); setOrgTenantId(org);
...@@ -1464,13 +1477,13 @@ public class OrgApplicationImpl extends BaseApplication implements OrgApplicatio ...@@ -1464,13 +1477,13 @@ public class OrgApplicationImpl extends BaseApplication implements OrgApplicatio
public void initPassword(String personId, String newPassword) { public void initPassword(String personId, String newPassword) {
Assert.hasText(personId, String.format(CommonDomainConstants.PARAMETER_NOT_NULL_FORMAT, "personId")); Assert.hasText(personId, String.format(CommonDomainConstants.PARAMETER_NOT_NULL_FORMAT, "personId"));
Person person = this.loadPerson(personId); Person person = this.loadPerson(personId);
Util.check(person != null, "没有找到ID“%s”对应的人员。", new Object[] { personId }); Util.check(person != null, "没有找到ID“%s”对应的人员。", new Object[]{personId});
String decodedPassword = new String(Base64.decodeBase64(newPassword)); String decodedPassword = new String(Base64.decodeBase64(newPassword));
person.setPassword(Md5Builder.getMd5(decodedPassword)); person.setPassword(Md5Builder.getMd5(decodedPassword));
this.personRepository.save(person); this.personRepository.save(person);
if(initPasswordListener!=null){ if (initPasswordListener != null) {
//发送邮件 //发送邮件
initPasswordListener.onInitPassword(person,decodedPassword); initPasswordListener.onInitPassword(person, decodedPassword);
} }
} }
...@@ -1490,7 +1503,7 @@ public class OrgApplicationImpl extends BaseApplication implements OrgApplicatio ...@@ -1490,7 +1503,7 @@ public class OrgApplicationImpl extends BaseApplication implements OrgApplicatio
if (person.getSecurityGrade() != null) { if (person.getSecurityGrade() != null) {
String decodedNewPassword = new String(Base64.decodeBase64(newPassword)); String decodedNewPassword = new String(Base64.decodeBase64(newPassword));
SecurityPolicy securityPolicy = this.securityPolicyApplication.findSecurityGrade(person.getSecurityGrade(), ValidStatus.ENABLED.getId()); SecurityPolicy securityPolicy = this.securityPolicyApplication.findSecurityGrade(person.getSecurityGrade(), ValidStatus.ENABLED.getId());
String securityGradeText = DictUtil.getDictionaryDetailText("securityGrade",person.getSecurityGrade()); String securityGradeText = DictUtil.getDictionaryDetailText("securityGrade", person.getSecurityGrade());
Assert.state(securityPolicy != null, String.format("密级“%s”没有设置或启用安全策略。", securityGradeText)); Assert.state(securityPolicy != null, String.format("密级“%s”没有设置或启用安全策略。", securityGradeText));
if (decodedNewPassword.length() < securityPolicy.getPasswordMinimumLength()) { if (decodedNewPassword.length() < securityPolicy.getPasswordMinimumLength()) {
......
...@@ -3,6 +3,8 @@ package com.huigou.uasp.bmp.configuration.application.impl; ...@@ -3,6 +3,8 @@ package com.huigou.uasp.bmp.configuration.application.impl;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.huigou.util.LogHome;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
...@@ -25,7 +27,7 @@ import com.huigou.uasp.bmp.configuration.repository.SysParameterRepository; ...@@ -25,7 +27,7 @@ import com.huigou.uasp.bmp.configuration.repository.SysParameterRepository;
* @author gongmm * @author gongmm
*/ */
@Service("parameterApplication") @Service("parameterApplication")
public class ParameterApplicationImpl extends BaseApplication implements ParameterApplication { public class ParameterApplicationImpl extends BaseApplication implements ParameterApplication, InitializingBean {
@Autowired @Autowired
private CodingGenerator codingGenerator; private CodingGenerator codingGenerator;
...@@ -84,4 +86,10 @@ public class ParameterApplicationImpl extends BaseApplication implements Paramet ...@@ -84,4 +86,10 @@ public class ParameterApplicationImpl extends BaseApplication implements Paramet
} }
} }
@Override
public void afterPropertiesSet() throws Exception {
LogHome.getLog(this).info("开始加载系统参数;");
this.syncCache();
LogHome.getLog(this).info("系统参数加载完成;");
}
} }
package com.huigou.uasp.bmp.plugin; package com.huigou.uasp.bmp.plugin;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.huigou.cache.SystemCache; import com.huigou.cache.SystemCache;
import com.huigou.cache.service.ICache; import com.huigou.cache.service.ICache;
import com.huigou.express.ExpressManager; import com.huigou.express.ExpressManager;
...@@ -23,7 +8,14 @@ import com.huigou.express.LoadExpressClasses; ...@@ -23,7 +8,14 @@ import com.huigou.express.LoadExpressClasses;
import com.huigou.util.ApplicationContextWrapper; import com.huigou.util.ApplicationContextWrapper;
import com.huigou.util.Constants; import com.huigou.util.Constants;
import com.huigou.util.DateUtil; import com.huigou.util.DateUtil;
import com.huigou.util.SpringBeanFactory; import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/** /**
* 系统初始化过滤器 * 系统初始化过滤器
...@@ -36,17 +28,18 @@ public class PlugInFilter implements Filter { ...@@ -36,17 +28,18 @@ public class PlugInFilter implements Filter {
ServletContext servletContext = filterConfig.getServletContext(); ServletContext servletContext = filterConfig.getServletContext();
String contextPath = servletContext.getContextPath(); String contextPath = servletContext.getContextPath();
String realPath = servletContext.getRealPath("/"); String realPath = servletContext.getRealPath("/");
ApplicationContextWrapper.init(WebApplicationContextUtils.getWebApplicationContext(servletContext)); ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
ApplicationContextWrapper.init(applicationContext);
try { try {
// 初始化系统数据缓存 // 初始化系统数据缓存
SystemCache.setCache(SpringBeanFactory.getBean(servletContext, "sysDataCache", ICache.class)); SystemCache.setCache(applicationContext.getBean("sysDataCache", ICache.class));
SystemCache.setRealPath(realPath); SystemCache.setRealPath(realPath);
SystemCache.setContextPath(contextPath); SystemCache.setContextPath(contextPath);
SystemCache.setStartTime(DateUtil.getDateFormat("yyyyMMddHH", DateUtil.getTimestamp())); SystemCache.setStartTime(DateUtil.getDateFormat("yyyyMMddHH", DateUtil.getTimestamp()));
initPath(SystemCache.getContextPath(), SystemCache.getRealPath());// 创建JS系统文件 initPath(SystemCache.getContextPath(), SystemCache.getRealPath());// 创建JS系统文件
ExpressManager.initExpress(SpringBeanFactory.getBean(servletContext, "expressUtil", ExpressUtil.class), ExpressManager.initExpress(applicationContext.getBean("expressUtil", ExpressUtil.class),
SpringBeanFactory.getBean(servletContext, "loadExpressClasses", LoadExpressClasses.class)); applicationContext.getBean("loadExpressClasses", LoadExpressClasses.class));
PlugInManager pm = SpringBeanFactory.getBean(servletContext, "plugInManager", PlugInManager.class); PlugInManager pm = applicationContext.getBean("plugInManager", PlugInManager.class);
pm.init(); pm.init();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
......
...@@ -10,11 +10,13 @@ import com.huigou.util.LogHome; ...@@ -10,11 +10,13 @@ import com.huigou.util.LogHome;
/** /**
* 系统参数插件 * 系统参数插件
* *
* @ClassName: ParameterPlugIn
* @author * @author
* @date 2014-2-24 上午10:51:46
* @version V1.0 * @version V1.0
* @ClassName: ParameterPlugIn
* @date 2014-2-24 上午10:51:46
* @deprecated 从1.2.11开始,在{@link com.huigou.uasp.bmp.configuration.application.impl.ParameterApplicationImpl}内部完成初始化缓存
*/ */
@Deprecated
@Service("parameterPlugIn") @Service("parameterPlugIn")
public class ParameterPlugIn implements StartPlugIn { public class ParameterPlugIn implements StartPlugIn {
......
package com.huigou.uasp.bpm.engine.application.impl; package com.huigou.uasp.bpm.engine.application.impl;
import java.util.ArrayList; import java.util.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.activiti.engine.FormService; import org.activiti.engine.FormService;
import org.activiti.engine.HistoryService; import org.activiti.engine.HistoryService;
...@@ -26,6 +22,7 @@ import org.activiti.engine.repository.ProcessDefinition; ...@@ -26,6 +22,7 @@ import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance; import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment; import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task; import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
...@@ -1290,7 +1287,14 @@ public class WorkflowApplicationImpl extends BaseApplication implements Workflow ...@@ -1290,7 +1287,14 @@ public class WorkflowApplicationImpl extends BaseApplication implements Workflow
queryModel.putParam(taskKind, taskKind); queryModel.putParam(taskKind, taskKind);
} }
} }
params.getObject("startTime0", Date.class).ifPresent(startTime0 -> {
sb.append(" and te.start_time_ >= :startTime0 ");
queryModel.putParam("startTime0", startTime0);
});
params.getObject("startTime1", Date.class).ifPresent(startTime1 -> {
sb.append(" and te.start_time_ <= :startTime1 ");
queryModel.putParam("startTime1", startTime1);
});
sql += sb.toString(); sql += sb.toString();
queryModel.setSql(sql); queryModel.setSql(sql);
Map<String, Object> result = this.sqlExecutorDao.executeSlicedQuery(queryModel); Map<String, Object> result = this.sqlExecutorDao.executeSlicedQuery(queryModel);
...@@ -1636,9 +1640,9 @@ public class WorkflowApplicationImpl extends BaseApplication implements Workflow ...@@ -1636,9 +1640,9 @@ public class WorkflowApplicationImpl extends BaseApplication implements Workflow
/** /**
* 获取任务节点信息 * 获取任务节点信息
* *
* @author
* @param sdo * @param sdo
* @return Map<String,Object> * @return Map<String, Object>
* @author
*/ */
public Map<String, Object> getProcedureInfo(String bizId, String procUnitHandlerId) { public Map<String, Object> getProcedureInfo(String bizId, String procUnitHandlerId) {
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
......
...@@ -47,7 +47,6 @@ ...@@ -47,7 +47,6 @@
<property name="plugIns"> <property name="plugIns">
<list> <list>
<ref bean="sysDictionaryPlugIn"/> <ref bean="sysDictionaryPlugIn"/>
<ref bean="parameterPlugIn"/>
<ref bean="tmspmPlugIn"/> <ref bean="tmspmPlugIn"/>
<ref bean="threeMemberPermissionPlugIn"/> <ref bean="threeMemberPermissionPlugIn"/>
<ref bean="applicationSystemPlugIn"/> <ref bean="applicationSystemPlugIn"/>
......
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