一. 版本控制
版本控制是指对软件开发过程中各种程序代码、配置文件及说明文档等文件变更的管理,是软件配置管理的核心思想之一。
版本控制最主要的功能就是追踪文件的变更。它将什么时候、什么人更改了文件的什么内容等信息忠实地了记录下来。每一次文件的改变,文件的版本号都将增加。除了记录版本变更外,版本控制的另一个重要功能是并行开发。软件开发往往是多人协同作业,版本控制可以有效地解决版本的同步以及不同开发者之间的开发通信问题,提高协同开发的效率。并行开发中最常见的不同版本软件的错误(Bug)修正问题也可以通过版本控制中分支与合并的方法有效地解决。
具体来说,在每一项开发任务中,都需要首先设定开发基线,确定各个配置项的开发初始版本,在开发过程中,开发人员基于开发基线的版本,开发出所需的目标版本。当发生需求变更时,通过对变更的评估,确定变更的影响范围,对被影响的配置项的版本进行修改,根据变更的性质使配置项的版本树继续延伸或产生新的分支,形成新的目标版本,而对于不受变更影响的配置项则不应发产生变动。同时,应能够将变更所产生的对版本的影响进行记录和跟踪。必要时还可以回退到以前的版本。例如当开发需求或需求变更被取消时,就需要有能力将版本回退到开发基线版本。在曾经出现过的季度升级包拆包和重新组包的过程中,其实就是将部分配置项的版本回退到开发基线,将对应不同需求的不同分支重新组合归并,形成新的升级包版本。
版本控制是软件配置管理的核心功能。所有置于配置库中的元素都应自动予以版本的标识,并保证版本命名的唯一性。版本在生成过程中,自动依照设定的使用模型自动分支、演进。除了系统自动记录的版本信息以外,为了配合软件开发流程的各个阶段。还需要定义、收集一些元数据来记录版本的辅助信息和规范开发流程,并为今后对软件过程的度量做好准备。当然如果选用的工具支持,这些辅助数据将能直接统计出过程数据,从而方便软件过程改进活动的进行。对于配置库中的各个基线控制项,应该根据其基线的位置和状态来设置相应的访问权限。一般来说,对于基线版本之前的各个版本都应处于被锁定的状态,如需要对它们进行变更,则应按照变更控制的流程来进行操作。
一个大型的项目,通常需要一个团队来协助完成开发,而这会面临一个什么样的问题?
1,代码的整合
2,代码的冲突问题
3,代码的版本控制;如何做备份,如何快速恢复到之前的某个版本,如何快速区分版本之间的差异等等;
以上的种种问题,都是我们在团队开发时会遇到的问题
目前来说,SVN和Git是两个主流的使用方式。
二. 项目开发
2.1 软件开发流程
软件开发流程即软件设计思路和方法的一般过程,包括对软件先进行需求分析,设计软件的功能和实现的算法和方法、软件的总体结构设计和模块设计、编码和调试、程序联调和测试以及编写、提交程序等一系列操作以满足客户的需求并且解决客户的问题,如果有更高需求,还需要对软件进行维护、升级处理,报废处理。
2.1.1 周期和阶段
从管理的角度,即从业务和经济的角度来看,软件的生命周期包括四个主要阶段。
起始阶段(Inception)-- 有一个好的想法:具体构想出终于产品的设想和它的业务案例,确定项目的范围。
细化阶段(Elaboration)--计划必要的活动和所需资源,具体确定功能并设计构架 。
构建阶段(Construction)--构建产品, 发展最初的设想、构架和计划,直到一个能够交付给用户的产品(完毕后的设想)完毕。
移交阶段(Transition)--将产品移交用户使用,包含:交付、培训、维护。
2.1.2 开发流程
2.1.2.1. 需求分析
产品经理向客户初步了解需求,然后用相关的工具软件列出要开发的系统的大功能模块,每个大功能模块有哪些小功能模块,对于有些需求比较明确相关的界面时,在这一步里面可以初步定义好少量的界面。
产品经理深入了解和分析需求,根据自己的经验和需求用WORD或相关的工具再做出一份文档系统的功能需求文档。这次的文档会清楚列出系统大致的大功能模块,大功能模块有哪些小功能模块,并且还列出相关的界面和界面功能。
产品经理向客户最终确认需求。
2.1.2.2.概要设计
首先,开发者需要对软件系统进行概要设计,即系统设计。概要设计需要对软件系统的设计进行考虑,包括系统的基本处理流程、系统的组织结构、模块划分、功能分配、接口设计、运行设计、数据结构设计和出错处理设计等,为软件的详细设计提供基础。
2.1.2.3.详细设计
在概要设计的基础上,开发者需要进行软件系统的详细设计。在详细设计中,描述实现具体模块所涉及到的主要算法、数据结构、类的层次结构及调用关系,需要说明软件系统各个层次中的每一个程序(每个模块或子程序)的设计考虑,以便进行编码和测试。详细设计应当足够详细,能够根据详细设计报告进行编码。
2.1.2.4.编码
在软件编码阶段,开发者根据《软件系统详细设计报告》中对数据结构、算法分析和模块实现等方面的设计要求,开始具体的编写程序工作,分别实现各模块的功能,从而实现对目标系统的功能、性能、接口、界面等方面的要求。在规范化的研发流程中,编码工作在整个项目流程里最多不会超过1/2,通常在1/3的时间,所谓磨刀不误砍柴功,设计过程完成的好,编码效率就会极大提高,编码时不同模块之间的进度协调和协作是最需要小心的,也许一个小模块的问题就可能影响了整体进度,让很多程序员因此被迫停下工作等待,这种问题在很多研发过程中都出现过。编码时的相互沟通和应急的解决手段都是相当重要的,对于程序员而言,bug永远存在,你必须永远面对这个问题!
2.1.2.5.测试
测试编写好的系统。交给用户使用,用户使用后一个一个的确认每个功能。软件测试有很多种:按照测试执行方,可以分为内部测试和外部测试;按照测试范围,可以分为模块测试和整体联调;按照测试条件,可以分为正常操作情况测试和异常情况测试;按照测试的输入范围,可以分为全覆盖测试和抽样测试。以上都很好理解,不再解释。总之,测试同样是项目研发中一个相当重要的步骤,对于一个大型软件,3个月到1年的外部测试都是正常的,因为永远都会有不可预料的问题存在。完成测试后,完成验收并完成最后的一些帮助文档,整体项目才算告一段落,当然日后少不了升级,修补等等工作,只要不是想通过一锤子买卖骗钱,就要不停的跟踪软件的运营状况并持续修补升级,直到这个软件被彻底淘汰为止。
2.1.2.6.软件交付
在软件测试证明软件达到要求后,软件开发者应向用户提交开发的目标安装程序、数据库的数据字典、《用户安装手册》、《用户使用指南》、需求报告、设计报告、测试报告等双方合同约定的产物。
《用户安装手册》应详细介绍安装软件对运行环境的要求、安装软件的定义和内容、在客户端、服务器端及中间件的具体安装步骤、安装后的系统配置。
《用户使用指南》应包括软件各项功能的使用流程、操作步骤、相应业务介绍、特殊提示和注意事项等方面的内容,在需要时还应举例说明。
2.1.2.7.验收
用户验收。
2.1.2.8.维护
根据用户需求的变化或环境的变化,对应用程序进行全部或部分的修改。
敏捷开发; 我先做一个产品,然后上线;接下来继续在这个产品的基础上去做修改添加等操作;
瀑布模型: 整个项目规划1-2年;每个月每个星期每天做什么事情; 整个业务整个项目做好了;上线提供给别人使用;
三. 项目介绍
蓝天CRM(客户关系管理)系统
企业级应用开发:我们的客户是针对于企业的,每一个使用项目的人,是企业里面的员工;
/*
CRM最大程度地改善、提高了整个客户关系生命周期的绩效。CRM整合了客户、公司、员工等资源,对资
源有效地、结构化地进行分配和重组,便于在整个客户关系生命周期内及时了解、使用有关资源和知识;简
化、优化了各项业务流程,使得公司和员工在销售、服务、市场营销活动中,能够把注意力集中到改善客户关
系、提升绩效的重要方面与核心业务上,提高员工对客户的快速反应和反馈能力;也为客户带来了便利,客户
能够根据需求迅速获得个性化的产品、方案和服务。
CRM不单是客户管理,还是销售管理、服务管理、财务管理、生产库存管理和采购管理的综合体,由此看
来,只要企业有销售环节,有自己的客户或者是销售渠道,就适合使用CRM
市场营销
客户关系管理系统在市场营销过程中,可有效帮助市场人员分析现有的目标客户群体,如主要客户群体集
中在哪个行业、哪个职业、哪个年龄层次、哪个地域等等,从而帮助市场人员进行精确的市场投放。客户关系
管理也有效分析每一次市场活动的投入产出比,根据与市场活动相关联的回款记录及举行市场活动的报销单据
做计算,就可以统计出所有市场活动的效果报表。
销售模块
销售是客户关系管理系统中的主要组成部分,主要包括潜在客户、客户、联系人、业务机会、订单、回款
单、报表统计图等模块。业务员通过记录沟通内容、建立日程安排、查询预约提醒、快速浏览客户数据有效缩
短了工作时间,而大额业务提醒、销售漏斗分析、业绩指标统计、业务阶段划分等功能又可以有效帮助管理人
员提高整个公司的成单率、缩短销售周期,从而实现最大效益的业务增长。
客户服务
客户服务主要是用于快速及时的获得问题客户的信息及客户历史问题记录等,这样可以有针对性并且高效
的为客户解决问题,提高客户满意度,提升企业形象。主要功能包括客户反馈、解决方案、满意度调查等功
能。应用客户反馈中的自动升级功能,可让管理者第一时间得到超期未解决的客户请求,解决方案功能使全公
司所有员工都可以立刻提交给客户最为满意的答案,而满意度调查功能又可以使最高层的管理者随时获知本公
司客户服务的真实水平。有些客户关系管理软件还会集成呼叫中心系统,这样可以缩短客户服务人员的响应时
间,对提高客户服务水平也起到了很好的作用。
权限管理[开发]
我们这个系统里面,不同的岗位的人,对整个系统的操作权限不一样;
淘宝(平台) 1:卖家 2:买家 3:淘宝(店小二)
前端页面
采用模板
全栈(前台页面要写,后台功能也要写,运维也要做,测试也要做)
*/
3.1 开发环境
3.2 主要功能
本系统的主要功能如下:
3.3 相关文档
3.4 项目展示
导入项目
导入数据库
运行
效果展示
四. 数据库设计
4.1 需求分析
就是我们一开始确定业务;需要知道保存哪些数据信息。
4.2 概念结构设计
结合需求,归纳和抽象;形成了一个独立于DMBS的概念模型。
4.3 逻辑结构设计
对概念模型进行优化;每个实体之间的逻辑关系。
ER图
4.4 物理结构设计
选取一种方式让上面的内容能够最合适的存储;存储结构和存储方法。
4.5 数据库实施
数据库和数据表建立出来。
4.6 运行和维护
数据库正常部署;DBA(数据库开发);维护。
4.7 数据库三范式
1范式:原子性;我们数据表的每一列不可在分割了。
2范式:主键;唯一性,代表我们这个数据表通过这个字段来区分每一条记录的唯一性。
3范式:主键关联;这个表的每个字段必须和主键有关系;不能是间接的传递关系;
4.8 数据表
1:管理员管理模块的表;
2:销售管理和客户管理
五. 框架搭建
5.1 创建项目
创建好项目;需要上传到svn。
5.2 工具类
常用的一些方法;针对字符串,密码加密;获取uuid等;
5.3 配置文件
数据库连接的一个配置文件;
5.4 页面模板
直接使用别人已经写好的页面;改成jsp页面;
5.5 BaseServlet
现在我们只要实现一个功能,我们就需要创建一个servlet!
例如:用户模块(登录,注册,退出登录,激活,发送邮件等等功能)。
也就是说,我们必须要创建一些系列的Servlet: UserLoginServlet –> 登录功能!
UserRegisterServlet –> 注册功能! 总之,只要你实现一个功能,你就得创建一个servlet!
这种情况会有两个弊端!
创建大量的servlet ! servlet实例 —> web容器(tomcat)管理!有大量的servlet实例必然会占有更大的运行内存!会间接的拖慢web容器的速度!
servlet 他的service(doget/dopost)方法是一个多线程方法!也就说理论上说!servlet可以并发的 处理请求!一个servlet能力很强!但是只对应一个功能!你不觉得浪费么?
一个servlet里面通过method的参数来判断执行哪个业务逻辑
如果一个servlet里面写了很多if else来判断;也不行
BaseServlet我们想解决的问题就是!简化和优化Servlet的创建和数量
可继承的方案
1: 我们每一个servlet继承BaseServlet
2: 需要在我们自己的servlet上面加上@WebServlet("/admin.lt")
package com.lant.www.web;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class BaseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1: 获取当前执行的哪个方法
String methodName = request.getParameter("method");
//没有告诉我们的servlet你要访问什么内容
if(null == methodName || "".equals(methodName)){
response.sendRedirect("index.jsp");
return;
}
//2: 获取当前的servlet是谁
// this;就代表当前访问的servlet对象
//3: 当前servlet对象,可以的得到他的字节码对象; 通过字节码对象获取方法对象
Class<? extends BaseServlet> cls = this.getClass(); //字节码对象
//4: 通过方法对象,执行目标方法;
Method method = null;
try {
//通过字节码对象获取了我们的方法
method = cls.getMethod(methodName, HttpServletRequest.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
if(null != method){
try {
//执行目标方法
//自己定义规则;自己去解析
//r# 我需要重定向
//z# 转发
//没有r#z# 代表当前是需要进行ajax处理
Object obj = method.invoke(this, request);
//代表方法没有返回值; 不符合我们自己定义的规则
if(null == obj || "".equals(obj)){
response.sendRedirect("index.jsp");
return;
}
String result = (String)obj;
String[] split = result.split("#");
if(split.length >= 2){
if("r".equalsIgnoreCase(split[0])){ //需要进行重定向
response.sendRedirect(split[1]);
}else if("z".equalsIgnoreCase(split[0])){ //代表转发
request.getRequestDispatcher(split[1]).forward(request,response);
}else{
//如果不是重定向,也不是转发;不行;出现运行时异常提醒你
throw new RuntimeException("你的字符串写错了");
}
}else{ //要做ajax请求
response.getWriter().print(result); //不要使用println
}
//执行完了目标方法;
//根据方法的返回值去做页面的跳转;
//1 重定向
//2 转发
//3 ajax请求 ; 不需要处理页面跳转 json信息
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}else{
//将请求转发到我们的首页; 没有告诉我们的servlet你要访问什么内容
response.sendRedirect("index.jsp");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
分发方案
DispatchServlet
1: 加载所有类里面的方法有XX注解的方法。
2: 用一个map保存对应的注解值和servlet方法的关系。
3: 来了请求,直接去map里面找到这个类和方法;通过反射去调用。
package com.lant.www.dispatch;
import com.lant.www.anno.GetParam;
import com.lant.www.bean.GetParamBean;
import com.lant.www.util.ClassUtil;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//分发的servlet
//@WebServlet("*.do") //配置处理我们所有的.do的请求
public class DispatchServlet implements Servlet {
//专门用来存放我们请求对应需要哪个实体类处理
private static Map<String,GetParamBean> config = new
HashMap<String,GetParamBean>();
//当我们servlet被创建的时候;代表会调用这个方法
@Override
public void init(ServletConfig servletConfig) throws ServletException {
//获取我们在web.xml里面配置的init-param参数
String packPath = servletConfig.getInitParameter("packPath"); //包名
/**
* 1: 通过包名获取包下面所有的类;
* 2: 找到这个类里面所有的方法
* 3: 找到方法上面所有的GetParam注解的内容
*/
//通过工具类的方法;获取指定包下面所有的字节码对象
List<Class> cls = ClassUtil.getClasssFromPackage(packPath);
//遍历我们的字节码集合对象 ; 获取这个里面的每一个有GetParam注解的方法;
for(Class c : cls){
// Method[] methods = c.getMethods(); //获取字节码对象里面所有的公共方法
Method[] methods = c.getDeclaredMethods();//获取自己类里面所有的方法
//判断这个方法是否有我们指定的注解
for(Method m : methods){
if(m.isAnnotationPresent(GetParam.class)){
GetParam annotation = m.getAnnotation(GetParam.class);
String requestPath = annotation.value(); //就是对应我们请求的Path
GetParamBean bean = new GetParamBean();
bean.setCls(c);
bean.setMethod(m);
//保存对应关系
config.put(requestPath,bean);
}
}
}
}
@Override
public ServletConfig getServletConfig() {
return null;
}
//有一个请求来到我们的这个service
//xx.do login.do
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest)servletRequest;
HttpServletResponse response = (HttpServletResponse)servletResponse;
//需要根据请求,去执行对应的目标方法;
//1: 获取当前请求的名称;
String uri = request.getRequestURI();
String path = uri.substring(uri.lastIndexOf("/"));
//2: 去config里面获取我们对应的方法;
GetParamBean bean = config.get(path);
if(null == bean){ //没有配置对应的请求地址
response.sendRedirect("error.jsp");
return;
}
//3: 执行
Method method = bean.getMethod();//需要执行的目标方法
Class cls = bean.getCls();//获取字节码对象
try {
Object o = cls.newInstance();//通过字节码对象得到一个实体类的对象
Object invoke = method.invoke(o, request);//执行目标方法,需要传递参数
String result = (String)invoke; //返回的结果
//4: 返回结果
GetParam annotation = method.getAnnotation(GetParam.class);
String type = annotation.type();
if("redirect".equalsIgnoreCase(type)){ // 重定向
response.sendRedirect(result);
}else if("ajax".equalsIgnoreCase(type)){ //代表当前是ajax请求
response.getWriter().print(result);
}else{
request.getRequestDispatcher(result).forward(request,response);
//默认转发
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
六. 业务开发
增删改查; crud; 对一个数据表做增删改查;
两套开发模型
先从数据库下手
1: 先写sql语句;对我们管理员表的操作;
2: 对应的数据表的业务模型(bean)和表对应;
3: 开发dao层的代码;
4: 开发service层的代码;
5: 开发web层的代码;(servlet)
6: 页面
-- 对应的dao里面的方法;
-- 查询所有的管理员信息
SELECT id,acount,pass,phone,age,createtime,updatetime,astatus,adesc FROM admin
-- 根据指定的条件查询管理员的信息
SELECT id,acount,pass,phone,age,createtime,updatetime,astatus,adesc FROM admin
WHERE acount LIKE "%a%" LIMIT 0,3
-- 查询一个管理员的信息(查询详情)
SELECT id,acount,pass,phone,age,createtime,updatetime,astatus,adesc FROM admin
WHERE acount = 'jack'
SELECT id,acount,pass,phone,age,createtime,updatetime,astatus,adesc FROM admin
WHERE id = 2
-- 根据指定条件删除
DELETE FROM admin WHERE id = 3
-- 修改管理员信息
UPDATE admin SET phone='111',age=20 WHERE id=3
-- 添加管理员信息
INSERT INTO admin(acount,pass,phone,age) VALUES('tom','123','13666666666',20)
先从页面下手;添加管理员
1: 先写页面
2: 页面需要配套我们的这个servlet处理。
3: 调用service去做实现。
4: 调用dao去保存到数据库。