#ZSCAT
springcloud 多商户商城 下载 https://gitee.com/catshen/zscat_sw
pc+h5 +小程序 +app uniapp集成 分销商城 积分商城
不用写xml配置!独特的日志记录方式!包含用户角色,权限等模块的后台管理系统!整合springmvc + mybatis-plus + beetl!
鸣谢
1.SpringBlade
2.beetl
3.mybatis-plus
##功能简介
- 用户管理
- 角色管理
- 菜单管理
- 部门管理
- 日志管理
- 监控管理
- 字典管理
- 通知管理
- 代码生成
##使用说明
- 导入sql/guns.sql文件到数据库
- 以maven方式导入项目,并修改数据库文件(src/main/resources/dev/jdbc.properties)为本机数据库配置
- 启动项目,管理员账号admin/密码111111
##所用框架
###前端
- bootstrap
- jquery
- bootstrap-table
- layer
- ztree
- webuploader
###后端
- springmvc
- mybatis
- mybatis-plus
- spring
- beetl
- hibernate-validator
- ehcache
##项目特点
- 零springxml配置,完全采用javabean方式配置spring,新思路,配置简洁,不易出错。详情请见com.stylefeng.guns.project.config包中具体类。
- 完善的日志记录体系,可记录登录日志,业务操作日志,异常日志到数据库,通过@BussinessLog注解和LogObjectHolder.me().set()方法,业务操作日志可具体记录哪个用户,执行了哪些业务,修改了哪些数据,并且日志记录为异步执行,详情请见@BussinessLog注解和LogObjectHolder,LogManager,LogAop类。
- 利用beetl模板引擎对前台页面进行封装和拆分,使臃肿的html代码变得简洁,更加易维护。
- 对常用js插件进行二次封装,使js代码变得简洁,更加易维护,具体请见webapp/static/js/common文件夹内js代码。
- 利用ehcache框架对经常调用的查询进行缓存,提升运行速度,具体请见ConstantFactory类中@Cacheable标记的方法。
- controller层采用map + warpper方式的返回结果,返回给前端更为灵活的数据,具体参见com.stylefeng.guns.modular.system.warpper包中具体类。
- 防止XSS攻击,通过XssFilter类对所有的输入的非法字符串进行过滤以及替换。
- 简单可用的代码生成体系,通过SimpleTemplateEngine可生成带有主页跳转和增删改查的通用控制器、html页面以及相关的js。
- 控制器层统一的异常拦截机制,利用@ControllerAdvice统一对异常拦截,具体见com.stylefeng.guns.core.aop.GlobalExceptionHandler类。
##零spring xml配置示例
以下配置示例仅列出部分spring配置,详情请见com.stylefeng.guns.project.config包中具体的配置类
###根配置
@Configuration
@ComponentScan(basePackages = {"com.stylefeng"}, excludeFilters = {
@Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class)// 这个是为了不让扫描到springmvc的控制器
})
@EnableAspectJAutoProxy
@Import(value = {DataSourceConfig.class, ShiroConfig.class, DruidMonitorConfig.class, EhcacheConfig.class})
public class RootSpringConfig {
}
###数据源配置
@Configuration
@EnableTransactionManagement
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfig implements EnvironmentAware {
private Environment em;
/**
* spring和MyBatis整合
*/
@Bean
public MybatisSqlSessionFactoryBean sqlSessionFactory(DataSource dataSource, GlobalConfiguration globalConfig) {
MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
sqlSessionFactory.setDataSource(dataSource);
sqlSessionFactory.setConfigLocation(new ClassPathResource("mybatis-config.xml"));
Resource[] classPathResources = ResKit.getClassPathResources("classpath*:com/stylefeng/guns/**/mapping/*.xml");
sqlSessionFactory.setMapperLocations(classPathResources);
//以下为mybatis-plus配置
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
paginationInterceptor.setDialectType("mysql");
sqlSessionFactory.setPlugins(new Interceptor[]{paginationInterceptor});
sqlSessionFactory.setGlobalConfig(globalConfig);
return sqlSessionFactory;
}
}
###零web.xml配置
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
// spring应用上下文
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{RootSpringConfig.class};
}
// springmvc 上下文
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
// 将DispatcherServlet映射到"/"
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
###springmvc配置
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.stylefeng.guns.**.controller", "com.stylefeng.guns.common.controller"})
@EnableAspectJAutoProxy
@Import({ControllerAopConfig.class})
public class SpringMvcConfig extends WebMvcConfigurerAdapter {
// beetl的视图解析器
@Bean
public BeetlSpringViewResolver beetlViewResolver() {
BeetlSpringViewResolver beetlSpringViewResolver = new BeetlSpringViewResolver();
beetlSpringViewResolver.setConfig(beetlConfiguration());
beetlSpringViewResolver.setContentType("text/html;charset=UTF-8");
beetlSpringViewResolver.setOrder(0);
return beetlSpringViewResolver;
}
// 配置静态资源的处理,对静态资源的请求转发到servlet容器中默认的servlet上(对静态资源的请求不做处理)
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
##业务日志记录原理
日志记录采用aop(LogAop类)方式对所有包含@BussinessLog注解的方法进行aop切入,会记录下当前用户执行了哪些操作(即@BussinessLog value属性的内容),如果涉及到数据修改,会取当前http请求的所有requestParameters与LogObjectHolder类中缓存的Object对象的所有字段作比较(所以在编辑之前的获取详情接口中需要缓存被修改对象之前的字段信息),日志内容会异步存入数据库中(通过ScheduledThreadPoolExecutor类)。
##beetl对前台页面的拆分与包装
例如,把主页拆分成三部分,每个部分单独一个页面,更加便于维护
<!--左侧导航开始-->
@include("/common/_tab.html"){}
<!--左侧导航结束-->
<!--右侧部分开始-->
@include("/common/_right.html"){}
<!--右侧部分结束-->
<!--右侧边栏开始-->
@include("/common/_theme.html"){}
<!--右侧边栏结束-->
以及对重复的html进行包装,使前端页面更加专注于业务实现,例如,把所有页面引用包进行提取
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="renderer" content="webkit" /><!-- 让360浏览器默认选择webkit内核 -->
<!-- 全局css -->
<link rel="shortcut icon" href="${ctxPath}/static/favicon.ico">
<!-- 全局js -->
<script src="${ctxPath}/static/js/jquery.min.js?v=2.1.4"></script>
<body class="gray-bg">
<div class="wrapper wrapper-content animated fadeInRight">
${layoutContent}
</div>
<script src="${ctxPath}/static/js/content.js?v=1.0.0"></script>
</body>
</html>
开发页面时,只需编写如下代码即可
@layout("/common/_container.html"){
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>部门管理</h5>
</div>
<div class="ibox-content">
//自定义内容
</div>
</div>
</div>
</div>
<script src="${ctxPath}/static/modular/system/dept/tBrand.js"></script>
@}
以上beetl的用法请参考beetl说明文档。
##对js常用代码的封装
在webapp/static/js/common目录中,有对常用js代码的封装,例如Feng.js,其中Feng.info(),Feng.success(),Feng.error()三个方法,分别封装了普通提示,成功提示,错误提示的代码,简化了layer提示层插件的使用。
##极简的图片上传方法
guns对web-upload进行二次封装,让图片的上传功能呢只用2行代码即可实现,如下
var avatarUp = new $WebUpload("avatar");
avatarUp.init();
具体实现请参考static/js/common/web-upload-object.js
##controller层,map+warpper返回方式介绍
map+warpper方式即为把controller层的返回结果使用BeanKit工具类把原有bean转化为Map的的形式(或者原有bean直接是map的形式),再用单独写的一个包装类再包装一次这个map,使里面的参数更加具体,更加有含义,下面举一个例子,例如,在返回给前台一个性别时,数据库查出来1是男2是女,假如直接返回给前台,那么前台显示的时候还需要增加一次判断,并且前后端分离开发时又增加了一次交流和文档的成本,但是采用warpper包装的形式,可以直接把返回结果包装一下,例如动态增加一个字段sexName直接返回给前台性别的中文名称即可。
##效果图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mqGqPaZl-1578551245281)(https://git.oschina.net/uploads/images/2017/0511/160059_b7a5d4d5_551203.jpeg “在这里输入图片标题”)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0JWZjFcV-1578551245282)(https://git.oschina.net/uploads/images/2017/0516/000735_b83c5c46_551203.png “在这里输入图片标题”)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cjT97O22-1578551245282)(https://git.oschina.net/uploads/images/2017/0511/160110_5e4f6d40_551203.jpeg “在这里输入图片标题”)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sduncxF2-1578551245283)(https://git.oschina.net/uploads/images/2017/0511/160117_94443161_551203.jpeg “在这里输入图片标题”)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PIE7rxjk-1578551245283)(https://git.oschina.net/uploads/images/2017/0511/160123_d0e287a5_551203.jpeg “在这里输入图片标题”)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yKxlBN1P-1578551245283)(https://git.oschina.net/uploads/images/2017/0511/160129_beb6b25c_551203.jpeg “在这里输入图片标题”)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ILvx3MYo-1578551245284)(https://git.oschina.net/uploads/images/2017/0511/160134_2212be4d_551203.jpeg “在这里输入图片标题”)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EkZG7HB3-1578551245284)(https://git.oschina.net/uploads/images/2017/0511/160139_ef118391_551203.jpeg “在这里输入图片标题”)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VhLle2j6-1578551245285)(https://git.oschina.net/uploads/images/2017/0511/160144_be4e3c3c_551203.jpeg “在这里输入图片标题”)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HGoNiJax-1578551245285)(https://git.oschina.net/uploads/images/2017/0511/160154_1e2bf378_551203.jpeg “在这里输入图片标题”)]
前台展示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IiFW0hHc-1578551245285)(https://git.oschina.net/uploads/images/2017/0607/093638_0f95fd60_791098.png “在这里输入图片标题”)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KDw2Tkyh-1578551245286)(https://git.oschina.net/uploads/images/2017/0607/093646_68bcb1fe_791098.png “在这里输入图片标题”)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rvLl9OtQ-1578551245286)(https://git.oschina.net/uploads/images/2017/0607/093655_80475b9b_791098.png “在这里输入图片标题”)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tIWEoo56-1578551245287)(https://git.oschina.net/uploads/images/2017/0607/093702_d176f4c2_791098.png “在这里输入图片标题”)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9xGxws2A-1578551245287)(https://git.oschina.net/uploads/images/2017/0607/093710_66f3995e_791098.png “在这里输入图片标题”)]
已完成的功能 后台权限管理 用户 角色 菜单
商城 前台页面 后台管理
此项目主要是学习redis使用,生产不建议这样用 ,应该结合数据库一起使用
此项目的后台权限管理采用ssm 通用mapper 做成,商城管理去掉了mybatis 全部采用redis做存储和增删该查
如 商品数据结构
-
- /**
-
-
* 用户基本信息
-
-
-
* zscat:goods:{id}:{pcid}:{ptid}
-
-
-
* hash
-
-
-
* {
-
-
-
* id: '100144', //ID
-
-
-
* title: '1', //标题
-
-
-
* tag: '0', //标签
-
-
-
* img: '1', //图片
-
-
-
* remark: '1,2', //内容
-
-
-
* summry: '0', //摘书
-
-
-
* }
-
-
-
*/
-
-
-
public static final String GOODS = "zscat:goods:%s:%s:%s";
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jN4n4qfh-1578551245287)(https://git.oschina.net/uploads/images/2017/0419/142316_c92a57a7_134431.png “在这里输入图片标题”)]
文章数据的redis 增删改查
public class ArticleRedis {
static RedisUtils RedisUtils = new RedisUtils(1);
/**
* 文章列表
*
* @param count
* @return
* @throws Exception
*/
public static List<Article> toArticle(int count) throws Exception {
Set<String> set = RedisUtils.keys(String.format(
RandomChatRedisKey.Article, "*"));
Iterator<String> it = set.iterator();
List<Article> l = new ArrayList<>();
int j = 0;
while (it.hasNext() && j < count) {
j++;
String key = it.next();
Map<String, String> map = RedisUtils.hgetall(key);
l.add((Article) BeanUtil.transMap2Bean(map, Article.class));
}
return l;
}
/**
* 保存或者修改文章
*
* @param count
* @return
* @throws Exception
*/
public static void saveArticleRedisDB(Article profile) {
String key = String.format(RandomChatRedisKey.Article, profile.getId());
Map<String, Object> map = BeanUtil.transBean2Map(profile, true);
Map<String, String> tempMap = new HashMap<>();
for (Map.Entry<String, Object> entry : map.entrySet()) {
tempMap.put(entry.getKey(), entry.getValue().toString());
}
RedisUtils.hmset(key, tempMap);
}
/**
* 删除文章
*
* @param count
* @return
* @throws Exception
*/
public static Long deleteArticleRedisDB(Article profile) {
String key = String.format(RandomChatRedisKey.Article, profile.getId());
return RedisUtils.del(key);
}
/**
* 获取文章文章
*
* @param count
* @return
* @throws Exception
*/
public static Article getArticleRedisDB(Long id) {
String key = String.format(RandomChatRedisKey.Article, id, "*");
Map<String, String> map = RedisUtils.hgetall(key);
return BeanUtil.transMap2Bean(map, Article.class);
}
}
创建数据库,启动redis
然后用tomcat启动项目 访问http://localhost:8081/zsRedis
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bpIjrbQy-1578551245288)(https://git.oschina.net/uploads/images/2017/0419/142759_b91b3b0c_134431.png “在这里输入图片标题”)]