共享体育智能销售系统开发
一.框架搭建
1.三大框架继承
Spring+SpringMVC+SpringDataJpa
1.1 Spring + SpringDataJpa 集成
<!--配置dataSource (数据源 )-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!--四大金刚-->
<property name="dataSource" ref="dataSource"/>
<!--配置包扫描-->
<property name="packagesToScan" value="cn.wing.aisell.domain"/>
<!--配置适配器-->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--方言-->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
<!--建表策略-->
<property name="generateDdl" value="false"/>
<!--显示sql-->
<property name="showSql" value="true"/>
</bean>
</property>
</bean>
<!--配置Spring对于JPA的事务支持-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!--支持事务注解-->
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- Spring Data Jpa配置 -->
<!-- base-package:扫描的包 -->
<jpa:repositories base-package="cn.wing.aisell.repository" entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager"
factory-class="cn.wing.aisell.repository.BaseRepositoryFactoryBean"
/>
1.2 Spring + SpringMVC 集成与配置
<!--配置springmvc的扫描-->
<context:component-scan base-package="cn.wing.aisell.web"/>
<!--支持注解-->
<mvc:annotation-driven/>
<!--支持静态访问-->
<mvc:default-servlet-handler/>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为1MB -->
<property name="maxUploadSize">
<value>10485760</value>
</property>
</bean>
<!-- Spring MVC 配置 -->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json; charset=UTF-8</value>
<value>application/x-www-form-urlencoded; charset=UTF-8</value>
</list>
</property>
<!-- No serializer:配置 objectMapper 为我们自定义扩展后的 CustomMapper,解决了返回对象有关系对象的报错问题 -->
<property name="objectMapper">
<bean class="cn.wing.aisell.common.CustomMapper"></bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
SpringMVC 在 web.xmL里面的配置
<!-- 配置解决中文乱码的问题 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置Spring启动的监听器,读取Spring的配置文件-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--配置核心控制器:读取SpringMVC配置文件,随服务器启动而启动-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2.完成基本模块简单的CRUD
2.1 Repository 层完成
- 使用SpringDataJpa完成 Repository 层的功能 (抽取BaseRepository)
- 高级查询功能 (抽取Query层) 完成对查询条件的封装
- 使用 jpa-spec插件 可以让我们更简单完成查询与分页和排序的功能
2.2 Service 层完成
- 创建 IBaseService 和 BaseServiceImpl 定义基本公用的功能
2.3 Controller 与 页面完成
- 完成 Controller 与 页面(页面使用EasyUI前端框架)
- 注意:No serializer异常的问题。SpringMVC与Jpa集成的时候(有懒加载)就会出现这个问题
解决方案
第一步:创建一个新的类(重写com.fasterxml.jackson.databind.ObjectMapper)
public class CustomMapper extends ObjectMapper {
public CustomMapper() {
this.setSerializationInclusion(JsonInclude.Include.NON_NULL);
// 设置 SerializationFeature.FAIL_ON_EMPTY_BEANS 为 false
this.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
}
}
第二步:在applicationContext-mvc.xml中配置一下
<!-- Spring MVC 配置 -->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json; charset=UTF-8</value>
<value>application/x-www-form-urlencoded; charset=UTF-8</value>
</list>
</property>
<!-- No serializer:配置 objectMapper 为我们自定义扩展后的 CustomMapper,解决了返回对象有关系对象的报错问题 -->
<property name="objectMapper">
<bean class="cn.wing.aisell.common.CustomMapper"></bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
- no session 的问题
解决方案 web.xml配置
<!--no session解决方案-->
<filter>
<filter-name>openEntityManager</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openEntityManager</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.4 前台展示与增删改基本功能
- 使用EasyUI的 datagrid 组件展示数据
- 添加修改的form 设置验证功能 (validatebox插件完成)
- 修改后数据丢失问题使用 SpringMVC的@ModelAttribute
- 关联对象修改 n-to-n 问题
@ModelAttribute("editEmployee")
public Employee beforeEdit(Long id, String cmd){
//有id的时候-> 修改功能
if(id!=null && "update".equals(cmd)) {
Employee employee = employeeService.findOne(id);
//把这个要修改的关联对象设置为null,可以解决n-to-n的问题
employee.setDepartment(null);
return employee;
}
return null;
}
2.5 模板技术与代码生产器
主流模板技术 两种 velocity 和 freemarker
项目中使用的Velocity
- 代码生成器 (EasyCode完成)
二.权限模块
Apache Shiro是一个强大且易用的Java安全框架
1.Shiro集成Spring
web.xmL中的配置
<!-- 过滤器 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
applicationContext-shiro.xml 配置
<!-- 创建securityManager这个核心对象 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- 引用自定义的aisellRealm -->
<property name="realm" ref="aisellRealm"/>
</bean>
<!-- 自定义realm-->
<bean id="aisellRealm" class="cn.wing.aisell.shiro.AisellRealm">
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!--加密方式-->
<property name="hashAlgorithmName" value="md5"/>
<!--加密次数-->
<property name="hashIterations" value="10"/>
</bean>
</property>
</bean>
<!-- 可以让咱们的权限判断支持【注解】方法 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!-- 核心权限过滤器 它的id和web.xml里面过去名称一样 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!-- 登录失败跳转的页面 -->
<property name="loginUrl" value="/login"/>
<!-- 登录成功跳转的页面 -->
<property name="successUrl" value="/s/main.jsp"/>
<!-- 没用权限跳转的页面 -->
<property name="unauthorizedUrl" value="/s/unauthorized.jsp"/>
<!-- 引用下面bean返回的map值 -->
<property name="filterChainDefinitionMap" ref="filterChainDefinitionMapBuilderMap"/>
<!--引用自定义拦截器-->
<property name="filters">
<map>
<entry key="aisellFilter" value-ref="aiSellPermissionsAuthorizationFilter"/>
</map>
</property>
</bean>
<!--引用自定义的bean并调用它里面的方法:这个方法回返回一个map -->
<bean id="filterChainDefinitionMapBuilderMap" factory-bean="filterChainDefinitionMapBuilder"
factory-method="createFilterChainDefinitionMap"/>
<!-- 配置自定义的baen -->
<bean id="filterChainDefinitionMapBuilder" class="cn.wing.aisell.shiro.FilterChainDefinitionMapBuilder"/>
<!--自定义拦截器-->
<bean id="aiSellPermissionsAuthorizationFilter" class="cn.wing.aisell.shiro.AiSellPermissionsAuthorizationFilter"/>
2.登录验证
- 准备密码加密算法工具类(密码的加密,与登录时候的验证)
- 准备前台登录界面 (通过Ajax请求提交登录请求)
- 自定义 Realm 类覆写 登录验证 与 权限控制 方法 (继承AuthorizingRealm类)
3.角色管理
- 完成前台角色显示页面
- 添加与修改 的时候 form 表单中 嵌套角色权限的增删
例如 :
- 保存(通过Ajax请求)
var url = "/role/save";
var id = $("#roleId").val();
if(id){
url = "/role/update?cmd=update";
}
roleForm.form('submit', {
url:url,
//这里我们加上params,可以通过这个参数修改咱们的提交数据
onSubmit: function(param){
//得到所有明细
var rows = userPermissionGrid.datagrid("getRows");
//准备传参数据(权限多条数据,是个数组)
//param.permissions = [];
for(var i=0;i<rows.length;i++){
//设置相应的数据值
param["permissions["+i+"].id"] =rows[i].id;
}
//做验证
return roleForm.form("validate");
},
success:function(data){
var result = JSON.parse(data);//转成相应的json数据
if(result.success) {
roleGrid.datagrid('reload');
roleDialog.dialog('close');
}else{
$.messager.alert('提示信息','操作失败!,原因:'+result.msg,"error");
}
}
})
}
}
4.权限
- 获取当前用户,根据当前用户读取该用户的权限
- 自定义 Realm 类中完成权限过滤
- 自定义权限过滤器(用来解决Ajax请求的权限路径)继承PermissionsAuthorizationFilter 类 覆写onAccessDenied方法
- applicationContext-shiro.xml配置中配置自定义过滤器
- 自定义FilterChainDefinitionMapBuilder(通过java代码控制拦截放行路径)
<!-- 核心权限过滤器 它的id和web.xml里面过去名称一样 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!-- 登录失败跳转的页面 -->
<property name="loginUrl" value="/login"/>
<!-- 登录成功跳转的页面 -->
<property name="successUrl" value="/s/main.jsp"/>
<!-- 没用权限跳转的页面 -->
<property name="unauthorizedUrl" value="/s/unauthorized.jsp"/>
<!-- 引用下面bean返回的map值 -->
<property name="filterChainDefinitionMap" ref="filterChainDefinitionMapBuilderMap"/>
<!--引用自定义拦截器-->
<property name="filters">
<map>
<entry key="aisellFilter" value-ref="aiSellPermissionsAuthorizationFilter"/>
</map>
</property>
</bean>
<!--引用自定义的bean并调用它里面的方法:这个方法回返回一个map -->
<bean id="filterChainDefinitionMapBuilderMap" factory-bean="filterChainDefinitionMapBuilder"
factory-method="createFilterChainDefinitionMap"/>
<!-- 配置自定义的baen -->
<bean id="filterChainDefinitionMapBuilder" class="cn.wing.aisell.shiro.FilterChainDefinitionMapBuilder"/>
<!--自定义拦截器-->
<bean id="aiSellPermissionsAuthorizationFilter" class="cn.wing.aisell.shiro.AiSellPermissionsAuthorizationFilter"/>
5.菜单
- 根据用户权限查询用户菜单
- 二级菜单的设计
@Override
public List<Menu> findByUser() {
//定义一个装父菜单的容器
List<Menu> parentMenus = new ArrayList<>();
//从session拿到当前用户
Employee employee = UserContext.getUser();
//拿到当前用户所有的子菜单
List<Menu> menus = menuRepository.findByUser(employee.getId());
menus.forEach( menu -> {
//通过子菜单拿到父菜单
Menu parentMenu = menu.getParent();
//判断父菜单容器是否已经添加父菜单
if(!parentMenus.contains(parentMenu)){
//添加父菜单到父菜单容器
parentMenus.add(parentMenu);
}
//为这个父菜单添加子菜单
parentMenu.getChildren().add(menu);
});
return parentMenus;
}
三.业务模块
1.导入导出
- 实现系统Excel导入导出的功能 (通过EasyPOI)
2.系统数据字典
系统的初始数据我们就可以称之为数据字典
(对表的抽取)例如:比如说我们系统中需有这么两张表:单位,品牌
3.采购订单
采购订单与订单明细是组合关系
- 页面展示
- 增加修改操作 (需要在同一个界面完成订单明细与采购订单的操作)效果如下
- 保存 需要注意传送给后台参数的格式
- 后台接收需要 一方与多方也建立联系
4.采购报表
1.表格报表
- 数据更加详细
- 使用 EasyUI(datagrid-groupview) 完成
- 完成高级查询功能
2.图形报表
- 效果更佳直接
- 使用HighChart 完成
- 需要在查询对象中自己拼接JPQL 查询 返回数据
- 完成带条件的不同图表
5.销售业务
1、发货清单:由工程技术部在设计通知单出来即时编制“发货清单”录入“销售订单”。并通知生产部审核。
2、发货通知:计划部跟踪销售合同,财务部收到符合发货条件的货款时,以书面形式通知计划部,计划部根据“销售订单”生成销售发货单,与《货款回收担保书》一起到财务部盖放行章。
3、发货:仓库根据销售订单备货,填写实发数量,发货员认可签字后,票据员在系统中进行审核生成销售出库单。
4、补货:由计划部填写发货单到财务部盖放行证章,发货流程同上。
5、发票:财务根据金税软件开具的发票在用友系统中录入发票数据,发票类型与实际发票保持一致,录入的发票保存时注意仓库的正确性,录入的发票保存后进行复核。
6、会计处理:在应收系统中对发票审核,收款时发票与收款单据核销处理,同时在库存管理中对发票生成的销售出库单据审核,在存货核算中对销售出库单据记帐,生成销售成本结转凭证。