网页模板
说明
通用的定义,也可以理解为可复用,从抽象角度数据形态一致,例如:分类、一级分类、N级分类,这类信息可以说数据形态上是一致的,就可以设计成通用模块,铭飞的完全通用的功能是通过模块编码来区分,再例如新闻有标题、商品有标题、博文有标题,从抽象的角度看标题是通用的,basic模块就是这样抽象出来的模块,至于差异化(不能通用)的数据信息,通过扩展的手段,主要是通过一对一关系进行扩展,而编写扩展业务代码时,也只需要编写差异化部分,相对来说减少了代码的编写量;
通用分类
表名:category
场景描述
- 适用所有多树型结构数据,只需要通过菜单里面定义不同多模块编码就可以重复适用分类的功能;
通用栏目
表名:basic_column
场景描述
- 通过扩展通过分类形成,增加来模版绑定、自定义模型绑定等功能,例如:文章分类、商品分类;
通用信息
表名:basic
场景描述
- 包含基本等标题、缩略图、创建人等信息,可扩展成文章、商品、博文等模块;
CURD
如果再次扩展通用功能,在保存、更新、删除等时候就需要调用对应被扩展功能的方法,方便业务级联数据的更新,对于查询只需要在扩展模块里进行表关联查询返回结果集就可以,如果要设计一个可扩展的通用功能也必须要提供子类对应的CURD方法。
分类
- saveCategory
- updateCatoegory
- deleteCategory
信息
- saveBasic
- updateBasic
- deleteBasic
后端框架
技术 | 名称 | 官网 |
---|---|---|
Spring Framework | 容器 | http://projects.spring.io/spring-framework |
SpringMVC | MVC框架 | http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc |
Apache Shiro | 安全框架 | http://shiro.apache.org |
Spring session | 分布式Session管理 | http://projects.spring.io/spring-session |
MyBatis | ORM框架 | http://www.mybatis.org |
PageHelper | MyBatis分页插件 | http://git.oschina.net/free/Mybatis_PageHelper |
Log4J | 日志组件 | http://logging.apache.org |
Maven | 项目构建 | http://maven.apache.org |
Elasticsearch | 分布式搜索引擎 | https://www.elastic.co |
Redis | 分布式缓存数据库 | https://redis.io |
前端框架
技术 | 名称 | 官网 |
---|---|---|
jQuery | 函式库 | http://jquery.com/ |
Bootstrap | 前端框架 | http://getbootstrap.com/ |
Bootstrap-table | Bootstrap数据表格 | http://bootstrap-table.wenzhixin.net.cn/ |
BootstrapValidator | 表单验证 | http://bootstrapvalidator.com/ |
Font-awesome | 字体图标 | http://fontawesome.io/ |
Waves | 点击效果插件 | https://github.com/fians/Waves/ |
zTree | 树插件 | http://www.treejs.cn/v3/ |
Select2 | 选择框插件 | https://github.com/select2/select2 |
jQuery EasyUI | 基于jQuery的UI插件集合体 | http://www.jeasyui.com/ |
Vue | MVVM框架 | https://cn.vuejs.org/ |
AmazeUI | 移动端UI | http://amazeui.org/ |
Plupload | 上传控件 |
|
开发工具
MySql: 数据库
jetty: 开发服务器
Tomcat: 应用服务器
Git: 版本管理
Eclipse: 开发IDE
Navicat Model: 建模工具
Navicat for MySQL: 数据库客户端
开发环境
Jdk7+
Mysql5.5+
Elasticsearch2.4.0
项目部署
参考 铭飞环境配置手册
资源下载
分层
分层的概念
代码分层就是让每一块代码专注于自己功能的实现:比如持久化层就只关心持久化的功能,而不取关心实体层那里应该有多少属性。
目前用到的分层有
实体层:entity
- 一个自定义的数据类型
持久化层:dao
- 持久化层它只关注实体化的工作,是接口
实现层:impl
- 实现层关注如何实现接口
通用工具:util
- 该层包含一些基础的公共工具
业务层:biz
- 业务层关注一个功能的具体业务,比如注册时你要判读这个用户是否是已经注册过,这个用户填写的资料是符合规范等等
控制层:action
- 控制层会调用业务层,实现一个具体的功能,这个功能可能是调用了好几个业务层,并且会提供方法访问地址。
分层原则
每个层次向外公开接口,但是隐藏内部细节
下一层为上一层服务,但不使用上层的服务
不同层之间通过实体类传输数据
数据库
命名
统一采用小写字母,通过每个单词之间使用下划线分割
- 表名——模块名业务名,例如:铭飞的cms模块里面的文章,表名 cms_article;
- 字段名——表名加字段名,例如:文章的内容,字段名article_content;
- 外键——fk_字段名,例如:文章的主键,fk_basic_id,推荐使用业务功能去实现主外键的业务;
- 索引——idx_字段名,例如:文章的标题,idx_basic_title;
- 主键——pk_字段名,例如:文章的编号,pk_basic_id;
必备字段
- id 主键
- create_date 创建时间
- update_date 更新时间
- create_by 创建人
- update_by 更新人
- del 删除标记
- app_id 应用编号,多应用下场景必须存在该字段,如果是扩展通用模型模块可以不需要,因为通用模型已存在该字段;
包
项目的包结构统一采用 组织域名.模块名 的方式,例如:铭飞的basic模块 net.mingsoft.basic ,mdiy模块 net.mingsoft.mdiy,
以下描述的都是在 组织域名.模块名 下创建
- 实体 entity 对应包 组织域名.模块名.entity
- 持久化 dao 对应包 组织域名.模块名.dao
- 业务 biz 对应包 组织域名.模块名.biz
- 业务 biz 实现 对应包 组织域名.模块名.biz.impl
- 控制 action 对应包 组织域名.模块名.action
- web 访客访问 对应包 组织域名.模块名.action.web
- people 会员访问 对应包 组织域名.模块名.action.people
- 常量 constant 对应包 组织域名.模块名.constant
- 资源 resources 对应 组织域名.模块名.resources
视图
视图页面统一存放在 WEB-INF 文件夹,例如:铭飞的basic管理 列表页WEB-INF/manager/basic/index.ftl 表单页WEB-INF/manager/basic/form.ftl
命名
- index.ftl 主界面、列表界面
- form.ftl 表单界面
实体 entity
继承
以下描述的是三种场景下的继承,普通模块已经存在通用字段定义,如:id、createBy、createDate等,更详细的参考实体的介绍
- basic的扩展模块(即basic模块+新模块组合成新的模块),继承 com.mingsoft.basic.entity,BasicEntity;
- 非basic扩展模块,但是需要区分应用数据(多站点),继承 com.mingsoft.basic.entity.BaseEntity;
- 普通模块,继承 com.mingsoft.base.eneity.BaseEntity;
命名
- 实体名与表名一致后面加Entity,例如:表名为news对应NewsEntity.java;
- 实体属性名必选与字段名、字段类型一致,例如:字段名为news_title对应属性定义 private String newsTitle;
属性处理
- 整型推荐使用integer;
相关注解
- DateTimeFormate :入参
JsonFormat :出参
时间类型要在属性之上添加@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")和 @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")注解。
注意事项:在实体中运用了时间的主界,在返回数据的list.do的方法中就避免用this.outjson 的方式返回数据,从而导致DateTimeFormat注解无效,在dao中resultmap避免用mybits的collection 和 association中的select属性去查询相关数据(暂时不支持)
相关实例:
持久化 dao
底层dao对CURD已经实现,如果模块是简单的CURD那么dao不需要编写代码,只需要在xml进行对应方法的SQL进行编写即可
继承
继承 com.mingsoft.base.dao.IBaseDao
命名
- 类名用大写i开头Dao结尾,同时创建名称一致的xml文件,例如:表名为news对应INewsDao.java;i表示接口类,对应xml文件
INewsDao.xml
XML对应方法
- saveEntity 对应 insert SQL语句
- updateEntity 对应 update SQL语句
- getEntity 对应 select SQL语句 ,当前实体为参数查询条件
- getByEntity 对应 select SQL语句,实体ID为查询条件
- deleteEntity 对应 delete SQL语句,实体为参数条件删除
- delete 对应 delete SQL语句,实体ID批量删除
- queryAll 对应 select SQL语句,查询所有
- query 对应 select SQL语句,实体为参数条件查询
业务 biz
继承
以下描述的是二种场景下的继承,底层对CURD进行了实现
- basic的扩展模块(即basic+新模块组合成新的模块),需要继承 com.mingsoft.basic.biz.IBasicBiz
- 普通模块,需要继承com.mingsoft.base.biz.IBaseBiz;
命名
业务主要分接口类与实现类,接口用大写i 头Biz结尾,实现类以Impl结尾,例如:表名为news对应INewsBiz.java、INewsBizImpl.java
控制 action
继承
继承 com.mingsoft.base.action.BaseAction
以下描述的是二种场景下保存、更新使用
- basic的扩展模块(即basic+新模块组合成新的模块),在保存与更新、删除必须调用父类的save、update方法,例如:扩展basic模块的业务对应调用 saveBasic、updateBasic、deleteBasic;
- 普通模块,在保存与更新、删除的时候直接调用底层的 saveEntity、updateEntity、delete方法
命名
以Action结尾,例如:表名为news对应NewsAction.java;
以下描述各个方法的命名,所有方法的传递推荐采用模块实体方式
- index 模块主页
- list 模块数据列表
- form 模块编辑表单
- get 获取模块数据
- save 保存
- delete 删除
- update 更新
返回数据
- 保存和更新直接返回实体;
- 如果返回的是实体,且包含时间格式属性,那么需要对时间格式化。比如: this.outJson(response, net.mingsoft.base.util.JSONArray.toJSONString(entity,new DoubleValueFilter(),new DateValueFilter("yyyy-MM-dd")));
- 如果在json转化成实体时,包含时间类型,且后端并不需要,那么可以在实体属性上方法中添加@JsonIgnore注解。
模块编码
- 模块编码由8位数组成,如:00000000 ,每两位分别表示:项目-模块-子功能-CURDO(查:0添:1删:2改:3等其他)
- 当添加权限模块时,模块编码可以填写 项目:模块:子功能:save|update|del|view 例如:article:save 表示信息的保存,结合权限控制使用
权限控制
方法
通过在方法上使用@RequiresPermissions注解,注解的值为模块编码的值, 如:@RequiresPermissions("article:save")。
表示文章模块的保存功能的权限控制。
视图按钮
使用<@shiro.hasPermission name="xxxx:xxxx">标签进行视图显示控制,如:
<@shiro.hasPermission name="article:save"><@ms.panelNavBtnAdd title=""/></@shiro.hasPermission>
业务开发
只需按以下五个步骤就能完成一个模块的基本功能开发,也可以使用铭飞提供的在线代码生成器功能快速生成以下业务代码。下面将通过扩展通用模型basic,快速开发一个博客模块,模块名定义为mblog。
博文的表结构:
CREATE TABLE `mblog_article` (
`article_basic_id` int NOT NULL COMMENT 'basic表主键',
`article_content` text NULL COMMENT '博文内容',
PRIMARY KEY (`article_basic_id`) ,
CONSTRAINT `fk_article_basic_id` FOREIGN KEY (`article_basic_id`) REFERENCES `basic` (`BASIC_ID`) ON DELETE CASCADE ON UPDATE NO ACTION
)
COMMENT = '博文表,通过扩展basic实现';
第一步:实体
package net.mingsoft.mblog.entity;
import com.mingsoft.basic.entity.BasicEntity;
import java.util.Date;
import org.springframework.data.elasticsearch.annotations.Document;
/**
* 博客文章实体
* @author 铭飞团队
* @version
* 版本号:100<br/>
* 创建日期:2015-6-20 16:05:42<br/>
* 历史修订:<br/>
*/
@Document(indexName = "blogarticle")
public class ArticleEntity extends BasicEntity {
private static final long serialVersionUID = 1500537942802L;
/**
* 博客文章自增长ID
*/
private Integer articleBasicId;
/**
* 文章内容
*/
private String articleContent;
public ArticleEntity(){}
public ArticleEntity(Integer articleBasicId) {
this.articleBasicId = articleBasicId;
}
public ArticleEntity(String articleContent) {
this.articleContent = articleContent;
}
/**
* 设置博客文章自增长ID
*/
public void setArticleBasicId(Integer articleBasicId) {
this.articleBasicId = articleBasicId;
}
/**
* 获取博客文章自增长ID
*/
public Integer getArticleBasicId() {
return this.articleBasicId;
}
/**
* 设置文章内容
*/
public void setArticleContent(String articleContent) {
this.articleContent = articleContent;
}
/**
* 获取文章内容
*/
public String getArticleContent() {
return this.articleContent;
}
}
第二步:持久化
IArticleDao.java
package net.mingsoft.mblog.dao;
import com.mingsoft.base.dao.IBaseDao;
import com.mingsoft.util.*;
import java.util.*;
import org.springframework.stereotype.Component;
import net.mingsoft.mblog.entity.ArticleEntity;
/**
* 博客文章持久层
* @author 铭飞团队
* @version
* 版本号:100<br/>
* 创建日期:2016-6-20 16:05:42<br/>
* 历史修订:<br/>
*/
@Component("blogArticleDao")
public interface IArticleDao extends IBaseDao {
}
IArticleDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="net.mingsoft.mblog.dao.IArticleDao">
<resultMap id="resultMap" type="net.mingsoft.mblog.entity.ArticleEntity">
<id column="article_basic_id" property="articleBasicId" /><!--博客文章自增长ID -->
<result column="article_content" property="articleContent" /><!--文章内容 -->
<result column="basic_title" property="basicTitle" /><!--文章内容 -->
</resultMap>
<!--保存-->
<insert id="saveEntity" useGeneratedKeys="true" keyProperty="articleBasicId"
parameterType="net.mingsoft.mblog.entity.ArticleEntity" >
insert into mblog_article
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="articleContent != null and articleContent != ''">article_content,</if>
<if test="basicId != null">article_basic_id,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="articleContent != null and articleContent != ''">#{articleContent},</if>
<if test="basicId != null">#{basicId},</if>
</trim>
</insert>
<!--更新-->
<update id="updateEntity" parameterType="net.mingsoft.mblog.entity.ArticleEntity">
update mblog_article
<set>
<if test="articleContent != null and articleContent != ''">article_content=#{articleContent},</if>
</set>
where article_basic_id = #{basicId}
</update>
<!--根据id获取-->
<select id="getEntity" resultMap="resultMap" parameterType="int">
select * from mblog_article where article_basic_id=#{articleBasicId}
</select>
<!--根据实体获取-->
<select id="getByEntity" resultMap="resultMap" parameterType="net.mingsoft.mblog.entity.ArticleEntity">
select * from mblog_article
<where>
<if test="articleContent != null and articleContent != ''"> and article_content=#{articleContent} </if>
</where>
limit 0,1
</select>
<!--删除-->
<delete id="deleteEntity" parameterType="int">
delete from mblog_article where article_basic_id=#{articleBasicId}
</delete>
<!--批量删除-->
<delete id="delete" >
delete from mblog_article
<where>
article_basic_id in <foreach collection="ids" item="item" index="index"
open="(" separator="," close=")">#{item}</foreach>
</where>
</delete>
<!--查询全部-->
<select id="queryAll" resultMap="resultMap">
select * from mblog_article order by article_basic_id desc
</select>
<!--条件查询-->
<select id="query" resultMap="resultMap" parameterType="net.mingsoft.mblog.entity.ArticleEntity">
select * from mblog_article
left join basic
on basic.basic_id = mblog_article.article_basic_id
<where>
<if test="basicPeopleId > 0 "> and basic_peopleid=#{basicPeopleId} </if>
<if test="articleContent != null and articleContent != ''"> and article_content=#{articleContent} </if>
</where>
order by article_basic_id desc
</select>
</mapper>
第三步:业务
IArticleBiz.java
package net.mingsoft.mblog.biz;
import com.mingsoft.basic.biz.IBasicBiz;
import com.mingsoft.basic.entity.BasicEntity;
import com.mingsoft.util.*;
import java.util.*;
import net.mingsoft.mblog.entity.ArticleEntity;
/**
* 博客文章业务接口
* @author 铭飞团队
* @version
* 版本号:100<br/>
* 创建日期:2015-6-20 16:05:42<br/>
* 历史修订:<br/>
*/
public interface IArticleBiz extends IBasicBiz {
}
ArticleBizImpl.java
package net.mingsoft.mblog.biz.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.mingsoft.base.biz.impl.BaseBizImpl;
import com.mingsoft.base.dao.IBaseDao;
import com.mingsoft.basic.biz.impl.BasicBizImpl;
import com.mingsoft.basic.entity.BasicEntity;
import com.mingsoft.util.*;
import java.util.*;
import net.mingsoft.mblog.entity.ArticleEntity;
import net.mingsoft.mblog.biz.IArticleBiz;
import net.mingsoft.mblog.dao.IArticleDao;
/**
*博客文章管理持久化层
* @author 铭飞团队
* @version
* 版本号:100<br/>
* 创建日期:2015-6-20 16:05:42<br/>
* 历史修订:<br/>
*/
@Service("blogArticleImpl")
public class ArticleBizImpl extends BasicBizImpl implements IArticleBiz {
@Resource(name="blogArticleDao")
private IArticleDao articleDao;
@Override
protected IBaseDao getDao() {
// TODO Auto-generated method stub
return articleDao;
}
}
第四步:控制
ArticleAction.java
package net.mingsoft.mblog.action;
import java.util.List;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.ui.ModelMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import net.mingsoft.mblog.biz.IArticleBiz;
import net.mingsoft.mblog.entity.ArticleEntity;
import net.mingsoft.mblog.search.IArticleSearch;
import net.mingsoft.mblog.search.mapping.ArticleMapping;
import net.mingsoft.base.elasticsearch.bean.BaseMapping;
import net.mingsoft.base.elasticsearch.bean.SearchBean;
import net.mingsoft.base.util.JSONObject;
import com.mingsoft.util.PageUtil;
import com.mingsoft.util.StringUtil;
import net.mingsoft.basic.util.BasicUtil;
import net.mingsoft.basic.util.ElasticsearchUtil;
import net.mingsoft.basic.bean.ListBean;
import com.mingsoft.base.filter.DateValueFilter;
import com.mingsoft.base.filter.DoubleValueFilter;
import com.mingsoft.basic.entity.BasicEntity;
import net.mingsoft.basic.bean.EUListBean;
/**
*后台博客文章控制层
* @author 铭飞团队
* @version
* 版本号:100<br/>
* 创建日期:2015-6-20 16:05:42<br/>
* 历史修订:<br/>
*/
@Controller("blogArticleAction")
@RequestMapping("/${managerPath}/mblog/article")
public class ArticleAction extends com.mingsoft.basic.action.BaseAction{
/**
* 注入博客文章业务层
*/
@Resource(name="blogArticleImpl")
private IArticleBiz articleBiz;
/**
* 返回主界面index
*/
@RequestMapping("/index")
public String index(HttpServletResponse response,HttpServletRequest request){
return view ("/mblog/article/index");
}
/**
* 查询博客文章列表
* @param article 暂无描述实体
* <i>article参数包含字段信息参考:</i><br/>
* articleBasicId 博客文章自增长ID<br/>
* articleContent 文章内容<br/>
* <dt><span class="strong">返回</span></dt><br/>
* <dd>[<br/>
* { <br/>
* articleBasicId: 博客文章自增长ID<br/>
* articleContent: 文章内容<br/>
* }<br/>
* ]</dd><br/>
*/
@RequestMapping("/list")
@ResponseBody
public void list(@ModelAttribute ArticleEntity article,HttpServletResponse response, HttpServletRequest request,ModelMap model) {
BasicUtil.startPage();
List articleList = articleBiz.query(article);
this.outJson(response, net.mingsoft.base.util.JSONArray.toJSONString(new EUListBean(articleList,(int)BasicUtil.endPage(articleList).getTotal()),new DoubleValueFilter(),new DateValueFilter()));
}
/**
* 返回编辑界面article_form
*/
@RequestMapping("/form")
public String form(@ModelAttribute ArticleEntity article,HttpServletResponse response,HttpServletRequest request,ModelMap model){
if(article.getArticleBasicId() != null){
BasicEntity articleEntity = (BasicEntity) articleBiz.getEntity(article.getArticleBasicId());
model.addAttribute("articleEntity",articleEntity);
}
return view ("/mblog/article/form");
}
/**
* 获取博客文章
* @param article 博客文章实体
* <i>article参数包含字段信息参考:</i><br/>
* articleBasicId 博客文章自增长ID<br/>
* articleContent 文章内容<br/>
* <dt><span class="strong">返回</span></dt><br/>
* <dd>{ <br/>
* articleBasicId: 博客文章自增长ID<br/>
* articleContent: 文章内容<br/>
* }</dd><br/>
*/
@RequestMapping("/get")
@ResponseBody
public void get(@ModelAttribute ArticleEntity article,HttpServletResponse response, HttpServletRequest request,ModelMap model){
if(article.getArticleBasicId()<=0) {
this.outJson(response, null, false, getResString("err.error", this.getResString("article.basic.id")));
return;
}
ArticleEntity _article = (ArticleEntity)articleBiz.getEntity(article.getArticleBasicId());
this.outJson(response, _article);
}
/**
* 保存博客文章实体
* @param article 博客文章实体
* <i>article参数包含字段信息参考:</i><br/>
* articleBasicId 博客文章自增长ID<br/>
* articleContent 文章内容<br/>
* <dt><span class="strong">返回</span></dt><br/>
* <dd>{ <br/>
* articleBasicId: 博客文章自增长ID<br/>
* articleContent: 文章内容<br/>
* }</dd><br/>
*/
@PostMapping("/save")
@ResponseBody
public void save(@ModelAttribute ArticleEntity article, HttpServletResponse response, HttpServletRequest request) {
//验证文章内容的值是否合法
if(StringUtil.isBlank(article.getArticleContent())){
this.outJson(response, null,false,getResString("err.empty", this.getResString("article.content")));
return;
}
articleBiz.saveBasic(article);//如果是普通模版 直接调用saveEntity(article)
this.outJson(response, JSONObject.toJSONString(article));
}
/**
* @param article 博客文章实体
* <i>article参数包含字段信息参考:</i><br/>
* articleBasicId:多个articleBasicId直接用逗号隔开,例如articleBasicId=1,2,3,4
* 批量删除暂无描述
* <dt><span class="strong">返回</span></dt><br/>
* <dd>{code:"错误编码",<br/>
* result:"true|false",<br/>
* resultMsg:"错误信息"<br/>
* }</dd>
*/
@RequestMapping("/delete")
@ResponseBody
public void delete(@RequestBody List<ArticleEntity> articles,HttpServletResponse response, HttpServletRequest request) {
int[] ids = new int[articles.size()];
for(int i = 0;i<articles.size();i++){
ids[i] = articles.get(i).getArticleBasicId();
}
articleBiz.deleteBasic(ids);
this.outJson(response, true);
}
/**
* 更新博客文章
* @param article 博客文章实体
* <i>article参数包含字段信息参考:</i><br/>
* articleBasicId 博客文章自增长ID<br/>
* articleContent 文章内容<br/>
* <dt><span class="strong">返回</span></dt><br/>
* <dd>{ <br/>
* articleBasicId: 博客文章自增长ID<br/>
* articleContent: 文章内容<br/>
* }</dd><br/>
*/
@PostMapping("/update")
@ResponseBody
public void update(@ModelAttribute ArticleEntity article, HttpServletResponse response,
HttpServletRequest request) {
//验证文章内容的值是否合法
if(StringUtil.isBlank(article.getArticleContent())){
this.outJson(response, null,false,getResString("err.empty", this.getResString("article.content")));
return;
}
articleBiz.updateBasic(article);//如果是普通模版 直接调用updateEntity(article)
this.outJson(response, JSONObject.toJSONString(article));
}
}
第五步:视图
index.ftl
<@ms.html5>
<@ms.nav title="暂无描述管理"></@ms.nav>
<@ms.searchForm name="searchForm" isvalidation=true>
<@ms.searchFormButton>
<@ms.queryButton onclick="search()"/>
</@ms.searchFormButton>
</@ms.searchForm>
<@ms.panel>
<div id="toolbar">
<@ms.panelNav>
<@ms.buttonGroup>
<@ms.addButton id="addArticleBtn"/>
<@ms.delButton id="delArticleBtn"/>
</@ms.buttonGroup>
</@ms.panelNav>
</div>
<table id="articleList"
data-show-refresh="true"
data-show-columns="true"
data-show-export="true"
data-method="post"
data-pagination="true"
data-page-size="10"
data-side-pagination="server">
</table>
</@ms.panel>
<@ms.modal modalName="delArticle" title="授权数据删除" >
<@ms.modalBody>删除此授权
<@ms.modalButton>
<!--模态框按钮组-->
<@ms.button value="确认删除?" id="deleteArticleBtn" />
</@ms.modalButton>
</@ms.modalBody>
</@ms.modal>
</@ms.html5>
<script>
$(function(){
$("#articleList").bootstrapTable({
url:"${managerPath}/mblog/article/list.do",
contentType : "application/x-www-form-urlencoded",
queryParamsType : "undefined",
toolbar: "#toolbar",
columns: [{ checkbox: true},
{
field: 'articleBasicId',
title: '博客文章自增长ID',
width:'10',
align: 'center',
formatter:function(value,row,index) {
var url = "${managerPath}/mblog/article/form.do?articleBasicId="+row.articleBasicId;
return "<a href=" +url+ " target='_self'>" + value + "</a>";
}
},{
field: 'basicTitle',
title: '文章标题',
width:'65'
},{
field: 'articleContent',
title: '文章内容',
width:'65,535',
formatter:function(value,row,index) {
var url = "${managerPath}/mblog/article/form.do?articleContent="+row.articleContent;
return "<a href=" +url+ " target='_self'>" + value + "</a>";
}
}]
})
})
//增加按钮
$("#addArticleBtn").click(function(){
location.href ="${managerPath}/mblog/article/form.do";
})
//删除按钮
$("#delArticleBtn").click(function(){
//获取checkbox选中的数据
var rows = $("#articleList").bootstrapTable("getSelections");
//没有选中checkbox
if(rows.length <= 0){
<@ms.notify msg="请选择需要删除的记录" type="warning"/>
}else{
$(".delArticle").modal();
}
})
$("#deleteArticleBtn").click(function(){
var rows = $("#articleList").bootstrapTable("getSelections");
$(this).text("正在删除...");
$(this).attr("disabled","true");
$.ajax({
type: "post",
url: "${managerPath}/mblog/article/delete.do",
data: JSON.stringify(rows),
dataType: "json",
contentType: "application/json",
success:function(msg) {
if(msg.result == true) {
<@ms.notify msg= "删除成功" type= "success" />
}else {
<@ms.notify msg= "删除失败" type= "fail" />
}
location.reload();
}
})
});
//查询功能
function search(){
var search = $("form[name='searchForm']").serializeJSON();
var params = $('#articleList').bootstrapTable('getOptions');
params.queryParams = function(params) {
$.extend(params,search);
return params;
}
$("#articleList").bootstrapTable('refresh', {query:$("form[name='searchForm']").serializeJSON()});
}
</script>
form.ftl
<@ms.html5>
<@ms.nav title="暂无描述编辑" back=true>
<@ms.saveButton onclick="save()"/>
</@ms.nav>
<@ms.panel>
<@ms.form name="articleForm" isvalidation=true>
<@ms.hidden name="basicId" value="${articleEntity.articleBasicId?default('0')}"/>
<@ms.text name="basicTitle" colSm="2" width="400" label="文章标题" title="文章标题" size="5" placeholder="请输入文章标题" value="${articleEntity.basicTitle?default('')}" validation={"maxlength":"300","required":"true", "data-bv-notempty-message":"文章标题不能为空","data-bv-stringlength-message":"标题在300个字符以内!"}/>
<@ms.text name="basicSort" colSm="2" width="200" label="自定义顺序" title="自定义顺序" size="5" placeholder="请输入文章顺序" value="${articleEntity.basicSort?c?default(0)}" validation={"data-bv-between":"true","data-bv-between-message":"自定义顺序必须大于0","data-bv-between-min":"0", "data-bv-between-max":"99999999","data-bv-notempty-message":"自定义顺序不能为空"}/>
<@ms.textarea colSm="2" name="basicDescription" label="描述" wrap="Soft" rows="4" size="" value="${articleEntity.basicDescription?default('')}" placeholder="请输入对该文章的简短描述,以便用户查看文章简略"/>
<@ms.textarea colSm="2" name="articleContent" label="文章内容" wrap="Soft" rows="4" size="" value="${articleEntity.articleContent?default('')}" />
</@ms.form>
</@ms.panel>
</@ms.html5>
<script>
var url = "${managerPath}/mblog/article/save.do";
if($("input[name = 'basicId']").val() > 0){
url = "${managerPath}/mblog/article/update.do";
$(".btn-success").text("更新");
}
//编辑按钮onclick
function save() {
$("#articleForm").data("bootstrapValidator").validate();
var isValid = $("#articleForm").data("bootstrapValidator").isValid();
if(!isValid) {
<@ms.notify msg= "数据提交失败,请检查数据格式!" type= "warning" />
return;
}
var btnWord =$(".btn-success").text();
$(".btn-success").text(btnWord+"中...");
$(".btn-success").prop("disabled",true);
$.ajax({
type:"post",
dataType:"json",
data:$("form[name = 'articleForm']").serialize(),
url:url,
success: function(status) {
if(status.result == true) {
<@ms.notify msg="保存或更新成功" type= "success" />
location.href = "${managerPath}/mblog/article/index.do";
}
else{
<@ms.notify msg= "保存或更新失败!" type= "fail" />
location.href= "${managerPath}/mblog/article/index.do";
}
}
})
}
</script>
底层代码重写
主要分视图代码重写、Java业务代码重写
视图代码重写只需要在当前开发项目中创建与底层视图文件路径、文件名一致的文件即可,例如:重写登录界面,只需要在当前项目创建/WEB-INF/manager/login.ftl文件,当项目发布的时候当前的login.ftl会覆盖底层提供的login.ftl;
-
后台业务代码的重写不像视图那样简单,需要在当前项目创建对应的类,如果类名出现相同,必须使用Spring注解定义bean名称,例如:@Controller("xxxx") @Service("xxxxxx")
方式使用小技巧
获取和填充session
我们在使用后台时,经常或碰到使用session中的数据,比如当前的管理员、当前的用户、当前的模块.....
这些功能我们都提供了相应的方法。但是使用时,需要继承相应模块。比如使用用户时,需要继承:net.mingsoft.people.action.BaseAction
- 用户的填充和获取
- 填充:this.setPeopleBySession(request, people); people是用户实体。
- 获取:this.getPeopleBySession(); 返回的是当前用户实体。只能在用户已经登录的情况下获取。
- 管理员的获取
- this.getManagerBySession(request); 返回的是管理员实体。只能在管理员登录后台后使用。
- 模块的获取
- this.getCategoryModelCode(request); 返回管理员当前的模块。
- 方法还有很多,可以参照铭飞后台API文档;
保存方法的妙用
获取主键自增长编号
- 我们在保存一条数据之后,有可能需要当前保存的实体自动增长的ID。这个时候就需要再使用保存的实体,再到数据库中查询一次。
- 我们考虑到了这里的复用性,所以我们在实体保存之后,会在当前实体中保存他数据库中保存的ID。所以说,我们在返回的实体中,是已经包含了自增长主键的实体。
保存自己的时候,同时保存父类。
- 有时,我们在保存当前实体时,需要先保存父类实体。一般的情况下,我们要调用两次方法。第一次保存父类,然后获取父类的ID,用来组织子类的数据。
- 我们考虑到了这里的通用性,所以我们可以在子类中调用父类的保存方法,已达到同时保存两张表的数据的同时,将父类的主键ID也传入子类的数据结构中。具体的实现方式可以参照快速开发步骤中的保存方法。
- 用户的填充和获取