| 项 | 值 |
|---|---|
| 项目名称 | jeeplus |
| 技术栈 | Jeeplus + Spring 4.0.8 + MyBatis 3.2.8 + MySQL 5.7 + Layui 2.0.3 |
| 打包方式 | WAR (jeeplus-0.0.1-SNAPSHOT.war) |
| Java | JDK 7/8 |
| ORM | MyBatis 3.2.8,XML 映射文件 |
| 前端 | JSP + Layui 2.0.3 + jQuery,自定义 jeeplus 标签库 |
| 工作流 | Activiti 5.x |
| 权限 | Apache Shiro 1.2.3 |
| 代码仓库 | Git,分支 master |
jeeplus.properties — 主配置(数据库、adminPath=/a、frontPath=/f 等)pom.xml — Maven 依赖管理src/main/resources/spring/ 目录com.jeeplus
├── common/ # 公共基础类
│ ├── config/Global.java # 全局配置常量
│ ├── persistence/
│ │ ├── BaseEntity.java # Entity 基类 (id, currentUser, page, sqlMap, isNewRecord)
│ │ ├── DataEntity.java # 数据实体基类 (remarks, createBy, createDate, updateBy, updateDate, delFlag)
│ │ ├── ActEntity.java # 工作流实体基类 (扩展 DataEntity, 含 Act act)
│ │ ├── CrudDao.java # DAO 接口基类 (get, findList, insert, update, delete)
│ │ ├── Page.java # 分页对象
│ │ └── annotation/MyBatisDao.java # MyBatis DAO 标记注解
│ ├── service/
│ │ ├── BaseService.java # Service 基类
│ │ └── CrudService.java # CRUD Service 基类 (泛型 D extends CrudDao, T extends DataEntity)
│ ├── web/BaseController.java # Controller 基类
│ └── utils/ # 工具类 (StringUtils, DateUtils, IdGen 等)
├── modules/ # 业务模块 (按功能分包)
│ └── {moduleName}/
│ ├── entity/ # 实体类
│ ├── dao/ # DAO 接口
│ ├── service/ # Service 类
│ ├── web/ # Controller 类
│ ├── enums/ # 枚举类 (可选)
│ ├── utils/ # 模块工具类 (可选)
│ └── thread/ # 线程类 (可选)
└── ...
文件位置: src/main/java/com/jeeplus/modules/{moduleName}/entity/{EntityName}.java
规范要点:
DataEntity<T>,含工作流的实体继承 ActEntity<T>(String id) 构造调用 super(id)private,提供 public getter/setter@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")@ExcelField(title="字段名", align=2, sort=序号)// 注释private static final long serialVersionUID = 1L;private WorkStaffBasicInfo basicInfo)Lists.newArrayList()(如 private List<WorkClientAttachment> workAttachments = Lists.newArrayList())/**
* Copyright © 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
*/
package com.jeeplus.modules.{moduleName}.entity;
// imports...
/**
* {模块中文名}Entity
* @author {作者}
* @version {日期}
*/
public class {EntityName} extends DataEntity<{EntityName}> {
private static final long serialVersionUID = 1L;
private String field1; // 字段注释
private Date field2; // 字段注释
public {EntityName}() {
super();
}
public {EntityName}(String id){
super(id);
}
@ExcelField(title="字段名", align=2, sort=7)
public String getField1() { return field1; }
public void setField1(String field1) { this.field1 = field1; }
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ExcelField(title="日期字段", align=2, sort=8)
public Date getField2() { return field2; }
public void setField2(Date field2) { this.field2 = field2; }
}
文件位置: src/main/java/com/jeeplus/modules/{moduleName}/dao/{EntityName}Dao.java
规范要点:
@MyBatisDao 注解CrudDao<T> 接口implements,只声明自定义方法@Param 注解/**
* Copyright © 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
*/
package com.jeeplus.modules.{moduleName}.dao;
import com.jeeplus.common.persistence.CrudDao;
import com.jeeplus.common.persistence.annotation.MyBatisDao;
import com.jeeplus.modules.{moduleName}.entity.{EntityName};
/**
* {模块中文名}DAO接口
* @author {作者}
* @version {日期}
*/
@MyBatisDao
public interface {EntityName}Dao extends CrudDao<{EntityName}> {
// 自定义查询方法
public List<{EntityName}> getByXxx(@Param("param1")String param1);
}
文件位置: src/main/resources/mappings/modules/{moduleName}/{EntityName}Dao.xml
规范要点:
namespace 必须等于 DAO 接口全限定名<sql id="{entityName}Columns"> 和 <sql id="{entityName}Joins"> 片段a.field AS "field")w.name AS "basicInfo.name")get, findList, findAllList, insert, update, delete, deleteByLogic, findUniqueByPropertypage.orderBy 动态 ORDER BY<if test="... != null and ... != ''"><if test="dbName == 'mysql'"> / oracle / mssql<?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="com.jeeplus.modules.{moduleName}.dao.{EntityName}Dao">
<sql id="{entityName}Columns">
a.id AS "id",
a.create_by AS "createBy.id",
a.create_date AS "createDate",
a.update_by AS "updateBy.id",
a.update_date AS "updateDate",
a.remarks AS "remarks",
a.del_flag AS "delFlag",
a.field1 AS "field1",
a.field2 AS "field2"
</sql>
<sql id="{entityName}Joins">
LEFT JOIN xxx_table x ON x.id = a.xxx_id
LEFT JOIN sys_office o ON o.id = a.office_id
LEFT JOIN sys_user u ON u.id = a.create_by
</sql>
<select id="get" resultType="{EntityName}">
SELECT <include refid="{entityName}Columns"/>
FROM {table_name} a
<include refid="{entityName}Joins"/>
WHERE a.id = #{id}
</select>
<select id="findList" resultType="{EntityName}">
SELECT <include refid="{entityName}Columns"/>
FROM {table_name} a
<include refid="{entityName}Joins"/>
<where>
a.del_flag = #{DEL_FLAG_NORMAL}
<if test="field1 != null and field1 != ''">
AND a.field1 = #{field1}
</if>
</where>
<choose>
<when test="page !=null and page.orderBy != null and page.orderBy != ''">
ORDER BY ${page.orderBy}
</when>
<otherwise>
ORDER BY a.update_date DESC
</otherwise>
</choose>
</select>
<insert id="insert">
INSERT INTO {table_name}(id, create_by, create_date, update_by, update_date, remarks, del_flag, field1, field2)
VALUES (#{id}, #{createBy.id}, #{createDate}, #{updateBy.id}, #{updateDate}, #{remarks}, #{delFlag}, #{field1}, #{field2})
</insert>
<update id="update">
UPDATE {table_name} SET
update_by = #{updateBy.id}, update_date = #{updateDate}, remarks = #{remarks},
field1 = #{field1}, field2 = #{field2}
WHERE id = #{id}
</update>
<update id="delete">
DELETE FROM {table_name} WHERE id = #{id}
</update>
<update id="deleteByLogic">
UPDATE {table_name} SET del_flag = #{DEL_FLAG_DELETE} WHERE id = #{id}
</update>
<select id="findUniqueByProperty" resultType="{EntityName}" statementType="STATEMENT">
select * FROM {table_name} where ${propertyName} = '${value}'
</select>
</mapper>
文件位置: src/main/java/com/jeeplus/modules/{moduleName}/service/{EntityName}Service.java
规范要点:
@Service 和 @Transactional(readOnly = true)CrudService<{EntityName}Dao, {EntityName}>@Transactional(readOnly = false)@Autowired,也可直接使用父类注入的 dao 字段dataScopeFilter() 放入 sqlMap.dsfsave() 方法时必须调用 super.save(entity) 并处理子表/**
* Copyright © 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
*/
package com.jeeplus.modules.{moduleName}.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.jeeplus.common.persistence.Page;
import com.jeeplus.common.service.CrudService;
import com.jeeplus.modules.{moduleName}.entity.{EntityName};
import com.jeeplus.modules.{moduleName}.dao.{EntityName}Dao;
/**
* {模块中文名}Service
* @author {作者}
* @version {日期}
*/
@Service
@Transactional(readOnly = true)
public class {EntityName}Service extends CrudService<{EntityName}Dao, {EntityName}> {
public {EntityName} get(String id) {
return super.get(id);
}
public List<{EntityName}> findList({EntityName} entity) {
return super.findList(entity);
}
public Page<{EntityName}> findPage(Page<{EntityName}> page, {EntityName} entity) {
return super.findPage(page, entity);
}
@Transactional(readOnly = false)
public void save({EntityName} entity) {
super.save(entity);
}
@Transactional(readOnly = false)
public void delete({EntityName} entity) {
super.delete(entity);
}
}
文件位置: src/main/java/com/jeeplus/modules/{moduleName}/web/{EntityName}Controller.java
规范要点:
@Controller + @RequestMapping(value = "${adminPath}/{moduleName}/{entityName}")BaseController,不能继承其他 Controller@ModelAttribute 方法根据 id 参数获取实体list → form → save → delete → export/import → 自定义@RequiresPermissions("{moduleName}:{entityName}:list")modules/{moduleName}/{entityName}List / Formredirect 回列表页,使用 Global.getAdminPath() 获取管理路径前缀addMessage(redirectAttributes, "消息") 传递消息MyBeanUtils.copyBeanNotNull2Bean(formObj, dbObj) 合并数据/**
* Copyright © 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
*/
package com.jeeplus.modules.{moduleName}.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.jeeplus.common.config.Global;
import com.jeeplus.common.persistence.Page;
import com.jeeplus.common.web.BaseController;
import com.jeeplus.common.utils.StringUtils;
import com.jeeplus.modules.{moduleName}.entity.{EntityName};
import com.jeeplus.modules.{moduleName}.service.{EntityName}Service;
/**
* {模块中文名}Controller
* @author {作者}
* @version {日期}
*/
@Controller
@RequestMapping(value = "${adminPath}/{moduleName}/{entityName}")
public class {EntityName}Controller extends BaseController {
@Autowired
private {EntityName}Service {entityName}Service;
@Autowired
private HttpServletRequest request;
@ModelAttribute
public {EntityName} get(@RequestParam(required=false) String id) {
{EntityName} entity = null;
if (StringUtils.isNotBlank(id)){
entity = {entityName}Service.get(id);
}
if (entity == null){
entity = new {EntityName}();
}
return entity;
}
@RequiresPermissions("{moduleName}:{entityName}:list")
@RequestMapping(value = {"list", ""})
public String list({EntityName} entity, HttpServletRequest request, HttpServletResponse response, Model model) {
Page<{EntityName}> page = {entityName}Service.findPage(new Page<{EntityName}>(request, response), entity);
model.addAttribute("page", page);
return "modules/{moduleName}/{entityName}List";
}
@RequestMapping(value = "form")
public String form({EntityName} entity, Model model) {
model.addAttribute("{entityName}", entity);
return "modules/{moduleName}/{entityName}Form";
}
@RequestMapping(value = "save")
public String save({EntityName} entity, Model model, RedirectAttributes redirectAttributes) throws Exception {
if (!beanValidator(model, entity)){
return form(entity, model);
}
if (!entity.getIsNewRecord()){
{EntityName} t = {entityName}Service.get(entity.getId());
MyBeanUtils.copyBeanNotNull2Bean(entity, t);
{entityName}Service.save(t);
} else {
{entityName}Service.save(entity);
}
addMessage(redirectAttributes, "保存成功");
return "redirect:" + Global.getAdminPath() + "/{moduleName}/{entityName}/?repage";
}
@RequestMapping(value = "delete")
public String delete({EntityName} entity, RedirectAttributes redirectAttributes) {
{entityName}Service.delete(entity);
addMessage(redirectAttributes, "删除成功");
return "redirect:" + Global.getAdminPath() + "/{moduleName}/{entityName}/?repage";
}
}
文件位置: src/main/webapp/webpage/modules/{moduleName}/{entityName}List.jsp / {entityName}Form.jsp / {entityName}View.jsp
规范要点:
<jsp:include> 和 Jeeplus 自定义标签库(taglib.jsp 引入)<form:form> 标签,modelAttribute 绑定后端对象${ctx} 获取上下文路径(来自 jeeplus.properties 的 adminPath)<table class="oa-table layui-table" id="contentTable"></table><table:page page="${page}"></table:page> 标签<table:sortColumn id="orderBy" name="orderBy" value="${page.orderBy}" callback="sortOrRefresh();"/>nav-btn nav-btn-add、nav-btn-refresh、nav-btn-import、nav-btn-exportopenDialogAdd()、openDialogEdit()、openDialogView() 函数<sys:message content="${message}"/>$(document).ready(function() {
// 表格列配置
var cols = [
{field:'no', title:'编号', sortable: true, width:100},
{field:'name', title:'名称', sortable: true},
{field:'createDate', title:'创建时间', sortable: true, width:150},
{fixed:'right', title:'操作', toolbar:'#operating', width:180}
];
var table = new TableInit("{entityName}", cols, '${ctx}/{moduleName}/{entityName}',
"searchForm", true, "contentTable", "operating");
table.init();
});
关键 JavaScript 组件构造器:
TableInit(tableName, cols, url, formId, checkFlag, tableId, toolId) — 初始化表格TableTreeInit(tableName, cols, url, formId, tableId, toolId, parentField) — 树形表格DetailTableInit(detailName, fieldName, cols, url, tableId, toolId, obj) — 子表Layui 日期控件: 使用 lay('.di').each(function(){ laydate.render({ elem: this, type: 'date', trigger: 'click' });})
BaseEntity.DEL_FLAG_NORMAL = "0" / DEL_FLAG_DELETE = "1" / DEL_FLAG_AUDIT = "2"Global.getAdminPath() — 管理端根路径(默认 /a)${ctx} — JSP 中引用 adminPathUserUtils.getUser() — 获取当前登录用户UserUtils.getSelectCompany() — 获取当前选中公司UserUtils.getSelectOffice() — 获取当前选中部门| 类型 | 命名模式 | 示例 |
|---|---|---|
| Entity | {EntityName}.java |
LeaveApply.java |
| DAO 接口 | {EntityName}Dao.java |
LeaveApplyDao.java |
| DAO XML | {EntityName}Dao.xml |
LeaveApplyDao.xml |
| Service | {EntityName}Service.java |
LeaveApplyService.java |
| Controller | {EntityName}Controller.java |
LeaveApplyController.java |
| 列表页 | {entityName}List.jsp |
leaveApplyList.jsp |
| 表单页 | {entityName}Form.jsp |
leaveApplyForm.jsp |
| 查看页 | {entityName}View.jsp |
leaveApplyView.jsp |
| 子表页 | {childName}List.jsp |
leaveDetailList.jsp |
{module_name} 如 leave_apply、leave_detail{ref_table}_id 如 staff_id、leave_idid(varchar 64), create_by, create_date, update_by, update_date, remarks, del_flagyyyy-MM-dd HH:mm:ss,前端 yyyy-MM-dd(日期控件)StringUtils.isNotBlank()/isBlank() 而非 != null && !""@RequiresPermissions("module:entity:action")@JsonIgnore 或 @JSONField(serialize = false)com.google.common.collect.Lists、com.google.common.collect.MapsHttpServletRequest request 用于获取当前请求ExportExcel/ImportExcel 工具类super.save(entity) 再分别处理子表 CRUD