简单的CRM项目

spring+springmvc+jpa的集成:
1.jar包的依赖
spring-web spring对web的支持
Spring-mvc SpringMVC的支持
Spring-orm spring对关系数据的支持
Spring-DataSource Spring对连接池的支持
hibernate对jpa的支持
mysql的包–对数据库的支持
Jackson的包–Spring对Jackson的支持
Spring-jdbc的包
Spring织物的包–切面
2.集成思路
jdbc.properties—>datasource—>EntityManagerfactory—>dao---->service---->controller
1.jdbc.properties 配置数据库的相关配置
文件:jdbc.properties 位置 resources

2.applicationContext.xml
文件:applicationContext  位置 resources
	1.连接池的配置	
	配置datasource
	2.扫描配置文件
	3.注解的支持---service repository
	4.jpa的配置 EntityManagerfactory
	5.事物的开启扫描和配置
	6.datajpa的配置

3.applicationContext-mvc.xml
文件:applicationContext-mvc  位置 resources
	1.mvc注解的配置
	2.静态资源配置
	3.包的扫描
	4.视图解析器的配置
	5.文件上传下载 名字唯一 multipartResolver
4.web.xml
文件:web.xml 位置WEB/INF
	1.dispatcherServlet的配置
		包含加载时间 文件加载applicationContext-mvc.xml
	2.文件监听器的配置
	3.文件的加载---applicationContext.xml
	4.编码问题

3.domain层的抽取
抽取商品的id为父类共有的字段
4.分页对象的抽取Query
父接口BaseQuery–》
字段:
crrentPage 默认是1 提供专供我们查询的0开始索引方法
pageSize 默认10
orderByType 默认true 升序
orderByName 默认无
方法:
createSort是否排序
抽象方法 createSpecification 要求子类按照自己的方式高级查询
子实现EmployeeQuery–>
字段
employee的字段
方法
覆写抽象方法createSpecification 按照username email age的高级查询

3.repository的抽取(dao层)—>datajpa功能扩展
分析见图SpringDataJpa的功能扩展.png
4.完成service层
思路:
父接口IBaseService—>CRUD 查询
父实现BaseServiceImpl—>开启事物 注入dao层BaseRepository 完成功能
IEmployeeService 继承IBaseService
EmployeeServiceImpl 实现接口IEmployeeService 继承BaseServiceImpl
5.mvc的配置
配置web.xml
controller层的建立(注意开启注解扫描)
6.页面的准备
main.jsp 中含有树状菜单tree(通过menu.json拿到数据)的layout布局,

layout布局

head.jsp的抽取
	把引入jquery的支持抽取出来 图标支持 css样式支持 中文支持 js支持 颜色支持 插件支持 
	引入<%@include file="/WEB-INF/views/head.jsp"%>
树菜单的tree
	读取写死的json数据
页签tabls
	在树状菜单中注册方法,点击添加一个页签,没有就新增,有就选中
		显示数据采用iframe显示:
		<iframe scrolling="auto" frameborder="0"  src="'+url+'" style="width:100%;height:100%;"></iframe>

employee.jsp
	显示employee数据
问题:---》easyui的数据显示与datajpa查询的数据不符合
解决:接受用employeeQuery对象新增两个方法接送前台传来的值
	新增一个对象,封装datajpa的数据返回的total rows

7.部门的展示
部门类及相关的准备
employee中配置外键
问题:
1.显示的图片为地址 使用formatter
value:当前列对应字段值。
row:当前的行记录数据。
index:当前的行下标。
直接方法方法返回值
2. no session
3.未序列化问题
8.高级查询
使用form包裹 方便使用jquery.jdirk.js的插件功能 直接将form中的属性封装成json对象
var params = searchForm.serializeObject();
employeeGrid.datagrid(‘load’,params);
问题:使用不了
解决方法 filename传参
9.删除功能
1.选中删除 未选中对话框 选中确认发生ajax请求
2.后台提供一个删除jsonresult对象 返回是否成功(private Boolean sucessful=true;//默认返回true 删除成功) 错误信息( private String mig;)
10.新增功能
1.将form表单放入dialog中
1.设置对话框dialog的属性为模态框 modal:true
每次点击弹出对话框 employeedialog.dialog(‘open’);
每次弹出的位置固定 employeedialog.dialog(‘center’);
2.数据验证功能
1.引入插件


2.年龄整数范围验证
data-options=“validType:‘integerRange[18,60]’”
3.密码二次确定验证
二次密码的一个验证框

注意密码的验证validType=“equals[‘password’,‘id’] //上一个验证框的id=password进行查找验证
4.自定义验证用户名
js中直接加
. e x t e n d ( .extend( .extend(.fn.validatebox.defaults.rules, {
checkName: {
validator: function(value,param){
var result = $.ajax({
url: “/employee/check”,
data:{username:value},
async: false//false发送同步请求
}).responseText;
return result===“true”;
},
message: ‘该用户名已经被占用,请重新输入.’
}
})
注意事项:(value,param) value为当前验证框输入值 param为传的参数数组
这里需要使用发送ajax同步请求 返回值为字符串
3.form重新开启应当自动清除上一次输入的内容
$(”#employeeform").form(‘clear’);

4.修改功能
	1.应该隐藏密码框和验证密码框
	在密码框 和验证密码框加属性 data-show
	增加中
	$(“*[data-show]”).show() //显示密码框
	$(“*[data-show] input”).validatebox("enable");//启用验证
	修改中:
	$(“*[data-show]”).hid()//隐藏密码框
	$(“*[data-show] input”).validatebox("disable");//禁用验证	
	2.修改应该验证用户名
		用户名为当前用户名可用,其他的为正常验证
		if(id!=null){
       				 if(employeeServiceImpl.findOne(id).getUsername().equals(username)){
       				     return true;
     				   }
  			  }
  			 return employeeServiceImpl.checkName(username);
5.保存功能
	使用from的保存方法
		$('#ff').form('submit', {
			url: ...,
			onSubmit: function(){
			var isValid = $(this).form('validate');//拿到验证内容
			if (!isValid){
				$.messager.progress('close');	// 如果表单是无效的则隐藏进度条
			}
			return isValid;	// 返回false终止表单提交
		},
		success: function(){
			$.messager.progress('close');	// 如果提交成功则隐藏进度条
		}
		});
	区别新增与修改:
		使用隐藏域传入id,通过id判断是新增(URL="/employee/save")修改(URL= "/employee/edict?cmd=cmd")
		后台通过不同的方法进行处理
	问题:
		1.数据丢失(修改保存数据,部门字段没有传入值,数据丢失)
		解决方法:
			1.使用隐藏域将值传入  保密性不好麻烦 
			2.在保存的时候自己使用jpql不改未变动的字段
			3.使用Spring的方法ModelAttribute
		 //这里准备一个方法,所有方法执行前都会执行它
			@ModelAttribute("editEmployee")
		 public Employee beforeEdit(Long id,String cmd){
		        //解决数据丢失问题,准备两个字段 修改传入参数才执行
		        if(id!=null&&cmd!=null){
		            Employee employee = employeeServiceImpl.findOne(id);
		            //把这个要修改的关联对象设置为null,可以解决n-to-n的问题
			         employee.setDepartment(null);
		           return employee;
		       }
		       return  null;
		   }
		在接受的参数@ModelAttribute("editEmployee")Employee employee,Spring会自动将修改的值保存到查出来的持久化对象里
		2.n to n的问题

11.代码生成器
模板技术velocity用来代码生成器,静态化页面、制作邮件,短信发送
可以是.vm结尾的模板
使用步骤:
1.导入相关的jar

org.apache.velocity
velocity
1.6

2.
//创建模板应用上下文
VelocityContext context = new VelocityContext();
context.put(“msg”, “小张是个好同志”);
//拿到相应的模板(需要设置好编码)
Template template = Velocity.getTemplate(“temptest/hello.html”,“UTF-8”);
//准备输出流(输出文件时使用文件字符流)
StringWriter writer = new StringWriter();
template.merge(context, writer);
System.out.println(writer);
代码生成器:
配置:导入plugins—>EasyCode-1.2.1-RELEASE.zip
步骤:1.创建自定义模板
2.使用velocity语法
3.注意在外面生成的路径为公共的如:包名cn.xxx.easysell 文件名E:/soft/software/idea/workspace/EasySell2/src
注意: 严格区别大小写
对表中不能直接生成代码(如抽取了父类的代码),要先筛选
当不确定列数,既有可能生成多列的要使用遍历
12.权限
密码—》加盐----》多次加密 基本不可破解
密码的验证:拿到密码进行相同的加密方式得到的结果进行判断
密码的修改:只能直接修改密码
权限:
权限与用户是多对多的关系 其中通过角色来串联
权限与资源有一对一 多对一(不用)一对多的关系
用户进入系统:角色可以是游客 游客在使用对应功能的时候需要令牌进行,系统应该根据用户的角色授权
shiro与Spring的集成:
.
applicationContext-shiro:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">



    <!-- Shiro's main business-tier object for web-enabled applications
         (use DefaultSecurityManager instead when there is no web environment)-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="jdbcRealm"/>
    </bean>


    <bean id="jdbcRealm" class="cn.xxx.easysell.shiro.jdbcRealm">
        <property name="name" value="jdbcRealm"/>
        <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"/>
    <!-- Enable Shiro Annotations for Spring-configured beans.  Only run after
         the lifecycleBeanProcessor has run: -->
    <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>

    <!-- Secure Spring remoting:  Ensure any Spring Remoting method invocations can be associated
         with a Subject for security checks. -->
    <bean id="secureRemoteInvocationExecutor" class="org.apache.shiro.spring.remoting.SecureRemoteInvocationExecutor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

    <!--loginUrl:没有登录,就会进入的页面
        successUrl:登录成功,进入这个页面
        unauthorizedUrl:没有权限,进入这个页面
        shiroFilter这个名字需要与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="/shiro/success.jsp"/>
        <property name="unauthorizedUrl" value="/shiro/unauthorized.jsp"/>
        <!-- 设置权限和资源
            annon不需要权限和登录也可以访问的页面
            authc需要登录才能访问
        -->
        <!--<property name="filterChainDefinitions">-->
            <!--<value>-->
                <!--/shiro/login.jsp= anon-->
                <!--/login=anon-->
                <!--/** = authc-->
            <!--</value>-->
        <!--</property>-->
        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"/>
    <property name="filters" >
        <map>
            <entry key="ajaxFilter" value-ref="ajaxPermissionsAuthorizationFilter"/>
        </map>
    </property>
</bean>

        <!--用来设置自定义权限拦截跳转-->
<bean id="ajaxPermissionsAuthorizationFilter" class="cn.xxx.easysell.common.AjaxPermissionsAuthorizationFilter"></bean>

    <!--用来设置权限的问题-->
    <bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapFactory" factory-method="createfilterChainDefinitionMap"/>
    <bean id="filterChainDefinitionMapFactory" class="cn.xxx.easysell.shiro.filterChainDefinitionMapFactory"/>

</beans>

13.登录
login重复请求不同请求方式问题:
在requestmapping注解中加入请求方式
@RequestMapping(value = “login”,method = RequestMethod.GET)
@RequestMapping(value = “login”,method = RequestMethod.POST)
请求发生ajax请求:
在发生登录验证请求的时候
解决二次带卡不是顶层页面的问题:
// 检查自己是否是顶级页面
if (top != window) {// 如果不是顶级
//把子页面的地址,赋值给顶级页面显示
window.top.location.href = window.location.href;
}
解决注销问题:
shiro提供了标签直接可以使用标签引出 注意引入shirotaglib
<%@ taglib prefix=“shiro” uri=“http://shiro.apache.org/tags” %>
shiro:user
欢迎[<shiro:principal />]登录,退出
</shiro:user>
解决回车登录问题:
$(document.documentElement).on(“keyup”, function(event) {
//console.debug(event.keyCode);
var keyCode = event.keyCode;
console.debug(keyCode);
if (keyCode === 13) { // 捕获回车
submitForm(); // 提交表单
}
});
登录过期,页面重叠问题:
// 检查自己是否是顶级页面
if (top != window) {// 如果不是顶级
//把子页面的地址,赋值给顶级页面显示
window.top.location.href = window.location.href;
}

14.多对多 角色和权限
配置:
用户与角色----》多对多的关系
在用户配置角色的外键(list)
角色与权限----》多对多的关旭
在角色配置角色的外键(list)
前台页面显示问题:
需求:在前台角色分页展示的时候,把角色对应的权限也显示出来
分析:已经配置好了权限的外键,角色的权限的id已经拿出对应权限的值(即通过中间表拿到权限的名字),只需要将名字拿出
问题:
1.取出角色对应的权限(是数组):在前台拿到的对应权限的为一个数组
解决:通过在权限提供一个方法将对应的权限数组遍历出来,然后将名字取出
<%–将角色对应的权限取出来–%>
权限
//遍历所有权限取出名字
function formatpermisson(value,row,index) {
var permisson="";
for(let per of value){
permisson+=" "+per.name;
}
return permisson;//formatter方法能够将返回的任意值显示
}

	需求:需要添加或者修改角色对应的权限
	分析:在弹出的添加框增加一个数据表格---》修改时:显示已知角色对应的权限(新增就为空)增加一个数据表格所有的权限(即权限数据表格)
	问题:
		1.弹出的时候才添加两个数据表格
			解决:将两个数据表格动态生成
perGrid.datagrid({
    url:'/permission/page',
    fit:true,
    fixed:true,
    fitColumns:true,
    pagination:true,
    /*双击增加*/
    onDblClickRow:addPermission


});
//加载左侧角色数据表格
beforegrid.datagrid({
    fit:true,
    fixed:true,
    fitColumns:true,
    /**双击减少*/
    onDblClickRow:removePermission
});
		2.双击增加权限表格新增 已经存在弹出提示 双击角色表格取消权限
			新增解决:
			权限表格--->双击增加触发事件---->获取列--->判断角色表格的列的id与获取的列是否相同  相同弹出提示 不相同新增
			取消解决:
			角色表格--->双击增加触发事件---->取消列(重新载入表格)
		3.关闭表格,依然显示有选择的权限
			新增打开应该讲角色数据表格数据置空
			//当前用户权限清空
			            beforegrid.datagrid("loadData",[]);
		4.修改表格,保存表格的时候取消权限,未保存,权限表格下次会减少找个权限
			原因:两个表格共用一个权限数据,应当操作两份数据
		var permissions = [];
           			 $.extend(permissions,row.permissions);//将权限表格的数据拷贝给角色表格操作
           			 beforegrid.datagrid("loadData",permissions);
		5.无法保存权限
			原因:后台的List 我们需要特别传参给后台

/*权限是一个数组 所以我们需要单独传参/
onSubmit: function(parm){//parm是我们传递到后台的参数
var rows=KaTeX parse error: Expected 'EOF', got '#' at position 3: ("#̲beforegrid").da…{i}].id`]=rows[i].id;
}
//拿到验证信息
var isValid = $(this).form(‘validate’);
if (!isValid){
$.messager.progress(‘close’); // 如果表单是无效的则隐藏进度条
}
return isValid; // 返回false终止表单提交
},
15.多对一 菜单的权限查询 自连接菜单的查询
分析:菜单与权限为多对一的关系 权限为多方(外键在权限)菜单为一方
1.在权限Permission配置外键Meun对象(多个权限对应一个菜单)
2.因为有需求:即通过菜单来查询多个菜单下的权限
在Permission配置menu的list,但是如果交给jpa管理的话,它会把这个菜单对应的权限全部查询出来(可能我只有这个菜单下的部门功能)
所以要脱离jpa管理@Transient
3.菜单存在子查询 即通过自己的父菜单查询到下面的子菜单
在menu配置子查询外键Menu parent 声明外键名字和抓取策略
4.需求:返回json形式的子父菜单json信息

	             分析:提供一个容器菜单  通过用户的id查询到子类菜单 在通过遍历子类菜单拿到父类菜单(通过外键查询到)判断容器菜单是否有此父菜单
			有就只添加子菜单

@Override
public List

getMenus(Long id) {
//准备的容器菜单—》用来装子菜单集合和父菜单
List meus=new ArrayList();

    //通过id查出所有子菜单
    List<Menu> childrenMenu = menuRepository.getChildrenMenu(id);

    //通过遍历子外键查询到对应的父菜单
    for (int i=0;i<childrenMenu.size();i++){
        //拿到子菜单
        Menu children = childrenMenu.get(i);
        //通过子菜单外键查询到父菜单
        Menu parent = children.getParent();
        if(meus.contains(parent)){

            parent.getChildren().add(children);
        }else {
            meus.add(parent);
            parent.getChildren().add(children);
        }
    }
    return meus;
}


注意:返回的字段必须是json格式 包括子节点名字children必须是children字段
	提供返回getText方法返回name

16.shiro对无效ajax请求定义显示undefined(在没有权限进行操作的时候)

需求:shiro对ajax无效请求显示undefined(在没有权限进行操作的时候)
分析:需要对ajax的无效请求单独处理,因为shiro默认处理没有权限的请求就是跳转页面,不符合ajax请求
	设置没有权限,对请求进行判断,如果是ajax请求就返回json数据
实现:

/**

  • PermissionsAuthorizationFilter:

  • 解决shiro对权限的拦截,发送ajax请求出现undefined问题
    /
    public class AjaxPermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {
    /
    *
    *
    *

    • @param request
    • @param response
    • @return
    • @throws IOException
      *思路:
    • 覆写onAccessDenied方法,通过判断请求头来确定是否是ajax请求
    • 在通过返回resp输出流输出输出json字符串(转移字符)
    • 配置

    */

    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
    Subject subject = this.getSubject(request, response);
    if (subject.getPrincipal() == null) {
    this.saveRequestAndRedirectToLogin(request, response);
    } else {
    //没有权限进入
    HttpServletRequest req= (HttpServletRequest) request;
    HttpServletResponse resp= (HttpServletResponse) response;
    String xRequestedWith = req.getHeader(“X-Requested-With”);
    //判断是否ajax请求LHttpRequest
    if(“XMLHttpRequest”.equals(xRequestedWith)){
    resp.setContentType(“javaScript/json; charset=UTF-8”);
    resp.getWriter().print("{“success”:false,“msg”:“没有权限做这个操作”}");
    System.out.println(11111);
    return false;
    }
    String unauthorizedUrl = this.getUnauthorizedUrl();
    if (StringUtils.hasText(unauthorizedUrl)) {
    WebUtils.issueRedirect(request, response, unauthorizedUrl);
    } else {
    WebUtils.toHttp(response).sendError(401);
    }
    }

     return false;
    

    }
    }

需要交给shiro管理:shiro.xml






    <!--用来设置自定义权限拦截跳转-->

注意在工厂设置权限的时候需要与配置key名字一致:
//通过读取数据库读取信息
List permissions = permissionService.findAll();
permissions.forEach(e->{
map.put(e.getUrl(), “ajaxFilter[”+e.getSn()+"]");
});
map.put("/**", “authc”);

17.办公文件的支持(POI对Excel的支持)
1.导入POI的包
2.下载
需求:根据用户的需要下载员工到数据库
思路: 1.在相关需要导出的类的字段打上注解
2.通过前台传来的query对象进行查询(复习:前台的高级查询,是通过将form中的表单信息,通过数据表格load方法,重新加载,然后将form中的对象封装在query复习,发送分页请求page来返回数据)
将query对象查询到员工集合,将员工集合返给poi对象 (注意图片需要通过拿到动态路径进行拼接)
3.POI的相关操作:
//设置一些属性
ExportParams params = new ExportParams(“员工管理”, “明细”, ExcelType.XSSF);
//params.setFreezeCol(3);
map.put(NormalExcelConstants.DATA_LIST, list); // 数据集合
map.put(NormalExcelConstants.CLASS, Employee.class);//导出实体
map.put(NormalExcelConstants.PARAMS, params);//参数
map.put(NormalExcelConstants.FILE_NAME, “employee”);//文件名称
//返回的名称 :easypoiExcelView -> 并没有找我的bean,而且当做一个路径去进行访问
// 现在默认去找的视图解析器,而没有找我的那一个bean
return NormalExcelConstants.EASYPOI_EXCEL_VIEW;//View名称
4.出现视图解析器的设置自动跳转页面,我们需要将返回的字符串找到指定的POI类,让他响应文件
配置bean的视图解析器,让他先找返回字符串是注解的在自动拼接

<context:component-scan base-package=“cn.afterturn.easypoi.view” />


3.上传
需求:用户能够上传表格到数据库,我们需要对里面的数据进行判断
思路:我们需要拿到里面的内容进行验证---->使用第三方jar和自定义验证–>将有效数据存入 返回无效数据提示
步骤:
1.JSR 303 规范验证包

org.hibernate hibernate-validator 5.2.4.Final 2.完成自定义验证功能 实现IExcelVerifyHandler注意mvc扫描包

15.订单模块
springmvc对日期:
前台时间显示get的时候加上@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”,timezone = “GMT+8”)
后台时间拿到在set方法@DateTimeFormat(pattern = “yyyy-MM-dd”)
订单和订单明细:为组合关系,强聚合,需要配置双向的一对多和多对一,最强级联和孤儿删除,而且需要双方都能找到对方,即
//从一方能找到多方 多方找到一方
//拿到一方的多方
List purchasebillitemList = purchasebill.getPurchasebillitems();
//遍历多方存入一方
for(Purchasebillitem item:purchasebillitemList){
//遍历拿到的所有明细 设置到保存到一方
item.setPurchasebill(purchasebill);
item.setAmount(item.getNum().multiply(item.getPrice()));
totalamount=totalamount.add(item.getAmount());
totalnum=totalnum.add(item.getNum());
}

SyntaxError: JSON.parse: unexpected end of data at line 1 column 668980 of the JSON data
问题来源:在进行订单的返回时,由于配置了双向的一对多和多对一,订单找明细 明细找订单的重复次数
解决:配置jsonigore忽略明细找订单
16.报表模块
提供数据表格(datagrid-groupview)和饼状图(HighChart)的报表模式:
数据表格的实现:通过datagrid-groupview实现
思路:1.数据需要单独一个对象来接受(避免过多无用数据的查询)----VO的使用(单独用来特殊功能的对象),装有我们需要的数据字段
2.前台通过对query对象赋值来 进行类型的数据展示(如按供应商、销售员、月份)报表,提供高级查询支持,将查询条件拼接,注意查询内容的位置
3.返回一个list装有我们的vo对象来进行展示

3d饼图的实现:通过highChart实现
思路:1.引入相关的js(注意js的路径在windows不区分大小写,引入失败找不到,需要查看target包下的路径是否正确)
2.发生ajax请求,将我们需要的高级查询条件传入,返回的值为饼状的data值(注意是一个键值形式)
3.我们的query对象需要的: 1.一个分组的jpql语句:通过判断前台传来的值来进行判断分组是通过 供应商 销售员 月份(月份使用MONTH(vdate)取出)
2.一个条件判断的语句:通过判断前台传来的值来进行StringBulider进行拼接,同时提供一个list对条件进行存储(因为条件需要通过?来进行赋值)
4.通过拼接的jpql(其中总计为:sum(小计)),传入lquery中的条件list(需要将其转换为数组,可变参数为数组,而不是需要一个一盒对象)
拿到装有一个object[]数组的list 遍历list将其值存入map中 key=“name” value=obj[0] key=“y” value=obj[1]
将map装入List返回前台
17.业务模块
基础模块 业务模块 其他模块 库存模块 销售模块 财务模块 物流模块
基础模块:员工管理 部门管理 数据字典
权限模块:基本一样 权限模块是可以直接用
进货模块、库存模块、销售模块(核心业务)

采购单的流程:
1.根据需求部门提出的需求提出采购申请表
即相关人员进入系统添加一致采购申请表(组合关系)
2.采购员找到供应商,询问价格,入库时间,保存一张咨询采购表
3.相关部门根据采购金额的大小进行评审,对供应商咨询采购产品价格列表。
4.进行采购环节,下采购单采购部经理审核,财务部有一个应付款
5.客户发货,发货单
6.准备采购入库单

财务模块:
应付款 应收款 (有零钱的结算忽略问题)

库存模块:
采购入库单、销售入库单、退货单、换货单、调货单、报损报溢单
盘点单

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值