-一、需求分析

-二、概要设计

-三、详细设计

    1.添加车辆

    2.实现租车业务

    3.实现还车业务

    数据层代码


-一、需求分析与数表设计

需求图:

wKiom1nEyPCiv6hyAAJH0lmmZA0317.png


发现类:

    01.moto类(汽车父类):

        01.1:bus类(客车类)

        01.2:car类(轿车类)

        01.3:truck类(卡车类)

    02.mototype类(汽车类型类)

    03.用户类:软件系统的使用者,登录该系统 管理的 人,比如:管理员、业务经理等。

    04.客户类:租车的人。

    05.公司类:用于处理租车换车业务流程的人。

-二、概要设计

数据库设计:

wKiom1nEzmKQO6VEAAI0x96Nd14181.png


-三、详细设计

  1. 实现各车的信息录入

wKioL1nE01ex5_gkAAB_b7VOqWo940.png

    一个问题:卡车跟大巴和轿车录入是不一样的,所以在业务层需要判断是什么车型,但这样代码比较臃肿,不利于扩展,不是面向对象编程。

    解决:采用面向对象的特点(继承和多态),在moto类建立录入车信息的方法,这样轿车客车大巴继承moto类就可以有自己的录入车信息的方法。


代码演示:

moto类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public abstract class Moto {
    private String mno;              //车牌号
    private int seatCount;       
    private MotoType mtype;            
     
 
    //这里省略get\set方法
     
    public Moto(MotoType mtype,String mno,int seatCount){
        this.mno = mno;
        this.seatCount = seatCount;
        this.mtype = mtype;
    }
     
    /**
     * 把当前对象存储到数据库中
     * @throws Exception
     */
    public void saveDB() throws Exception{
        CompanyDao dao = new CompanyDao();
        try {
            dao.addMoto(this);
        catch (Exception e) {
            e.printStackTrace();
        }finally{
            dao.closeConnection();
        }
    }
}

car类:可以直接使用moto类中的方法

1
2
3
4
5
public class Car extends Moto{
    public Car(MotoType mtype,String mno) {
        super(mtype,mno, 5);
    }
}

truck类:重写了moto类的saveDB()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class Truck extends Moto{
    private int dun;
    private double priceEachDun;            //每吨每天的单价
 
    public int getDun() {
        return dun;
    }
    public double getPriceEachDun() {
        return priceEachDun;
    }
    public void setPriceEachDun(double priceEachDun) {
        this.priceEachDun = priceEachDun;    
    }
    public Truck(MotoType mtype, String mno, int seatCount,int dun) {
        super(mtype, mno, seatCount);
        this.dun = dun;      
    }
    public double getDayMoney() {
        return priceEachDun*dun;
    }
     
    /**
     * 把当前对象存储到数据库中  ----------重写
     * @throws Exception
     */
    @Override
    public void saveDB() throws Exception{
        CompanyDao dao = new CompanyDao();
        try {
            dao.beginTransaction();
            dao.addMoto(this);
            TruckEntity truckEntity = new TruckEntity();
            truckEntity.setMno(this.getMno());
            truckEntity.setDun(dun);
            truckEntity.setPriceEachDun(priceEachDun);
            dao.addTruck(truckEntity);
            dao.commit();
        catch (Exception e) {
            e.printStackTrace();
            dao.rollback();
            throw e;
        }finally{
            dao.closeConnection();
        }
    }
}

在业务逻辑层方法就很简单了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private List<Moto> motos;
/**
* 添加汽车
* @param moto
* @throws Exception
*/
public void addMoto(Moto moto) throws Exception{
    if(moto != null ){          
        moto.saveDB();                      //OO多态
        motos.add(moto);
    }else{
        throw new Exception("入参moto错误");
    }      
}


2.实现租车业务

图解:

wKioL1nE1NrReZ66AAFU_AKkhqM429.png

    注意:由于整个租车过程包含多个表的修改,比如加入两个订单在差不多时间操作同一辆车,肯定会有一个订单有问题,这是就需要保证事务的一致性和完整性。


在业务逻辑层的代码就可以:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public class RentCompany {
     
    private String name; 
    private List<Moto> motos;               //待租赁的汽车
     
    public String getName() {
        return name;
    }  
 
    public List<Moto> getMotos() {
        return motos;
    }
 
    public RentCompany(String name){
         
        this.name = name;
        motos = new ArrayList<Moto>(50);
         
    }  
     
    /**
     * 汽车租赁
     * @param motos
     * @param client
     * @param rentinfo
     * @return            租赁成功,返回订单号
     * @throws Exception
     */
    public String rent(List<Moto> motos,TClient client,TRentInfo rentinfo) throws Exception{
        String rentno = null;
         
        if(motos != null && client != null && rentinfo!= null){
            CompanyDao dao = new CompanyDao();
            try {
                dao.beginTransaction();                                 //开启事务
                //添加客户数据       
                dao.addTClient(client);
                //添加汽车租赁信息
                rentno = dao.addRentInfo(rentinfo);
                //添加租赁明细
                for(Moto moto : motos){
                    TRentDetail rentDetail = getTRentDetail(rentno,moto);
                    dao.addRentDetail(rentDetail);
                }          
                dao.commit();                                         //提交事务  
            catch (Exception e) {
                dao.rollback();                                       //回滚事务
                rentno = null;
                throw e;
            }finally{
                dao.closeConnection();
            }      
        }else{         
            throw new Exception("入参错误,请检查");
        }      
         
        return rentno;
    }
     
    private TRentDetail getTRentDetail(String rentno,Moto moto){
        TRentDetail detail = new TRentDetail();
         
        detail.setMno(moto.getMno());
        detail.setRentno(rentno);
        detail.setDaymoney(moto.getDayMoney());
         
        return detail;
    }  
}

3.实现还车业务

业务流程:计算租金总额、添加还车信息、给订单写入租金总额、设置车辆信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 还车,计算租赁的总价
* @param moto
* @return
*/
public double rentBack(String rentno) throws Exception{
    double allMoney = 0;
 
    if(rentno != null){
        CompanyDao dao = new CompanyDao();
        try {
                dao.beginTransaction();
            //计算租金总额
            allMoney = dao.countAllPayMoney(rentno);
            //添加还车信息
            dao.addRentBackInfo(rentno);
            //给订单写入租金总额
            dao.writeAllPayMoney(rentno, allMoney);
            //设置车辆的状态
            dao.setRentBackMotoState(rentno);
            dao.commit();              
        catch (Exception e) {
            dao.rollback();
            throw e;
        }finally{
            dao.closeConnection();
        }          
    }else{
        throw new Exception("入参错误");
    }
 
    return allMoney;      
}


对应dao层代码:

basedao

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class BaseDao {
    protected Connection conn;       
    public Connection getConn() {
        return conn;
    }
 
    public void setConn(Connection conn) {
        this.conn = conn;
    }  
 
    public void openConnection() throws ClassNotFoundException,SQLException{
         
            //通过反射技术,对oracel的驱动对象进行类的加载 (其实是在做类型检查)
            //在类的加载时,会调用OracleDriver中的静态代码块和静态变量初始化
        if(this.conn == null || this.conn.isClosed()){
            Class.forName("oracle.jdbc.driver.OracleDriver");  
            conn = DriverManager.getConnection("jdbc:oracle:thin:@10.0.19.252:1521:orcl","testdb","1111");
        }  
         
    }
     
    public void beginTransaction() throws Exception{
        this.openConnection();
        if(this.conn != null){           
            this.conn.setAutoCommit(false);                      //手动提交模式
        }      
    }
     
    public void commit() throws Exception{
        if(this.conn != null){
            this.conn.commit();
        }
    }
     
    public void rollback() throws Exception{
        if(this.conn != null){
            this.conn.rollback();
        }
    }
     
    public void closeConnection() {
        if(this.conn != null){
            try {
                this.conn.close(); 
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
public class CompanyDao extends BaseDao{
    /**
     * 根据订单号,计算需要的租金总额
     * @param rentno
     * @return
     * @throws Exception
     */
    public double countAllPayMoney(String rentno) throws Exception{
         
        double allMoney = 0;
        double dayMoneys = 0;
 
        String sql = " select  sum(daymoney) dayMoneys from trentinfo r,trentdetail d where r.rentno = d.rentno"
                    " and r.rentno=?";
        this.openConnection();
        PreparedStatement ps = this.conn.prepareStatement(sql);
        ps.setString(1,rentno);
        ResultSet rs = ps.executeQuery();
        while(rs.next()){
            dayMoneys = rs.getDouble("dayMoneys");
        }
        RentinfoDto rentinfo = getRentInfo(rentno);
        int days = (int)((new Date()).getTime() - rentinfo.getRentBeginDate().getTime())/(24*3600*1000) + 1;
          
        allMoney = dayMoneys*days;
         
        return allMoney;
         
    }
     
    /**
     * 根据订单编号,返回订单信息
     * @param rentno
     * @return
     */
    public RentinfoDto getRentInfo(String rentno) throws Exception{
        RentinfoDto rentinfo = null;
         
        String sql = " select r.rentno,r.clno,r.eno,r.operator,r.rentbengindate,r.diyamoney,r.payallmoney, b.backdate  " +
                " from trentinfo r left join  trentback b on  r.rentno = b.rentno and r.rentno=?";
        this.openConnection();
        PreparedStatement ps = this.conn.prepareStatement(sql);
        ps.setString(1,rentno);
        ResultSet rs = ps.executeQuery();
        while(rs.next()){
            rentinfo = new RentinfoDto();
            rentinfo.setBackDate(rs.getDate("backdate"));
            rentinfo.setClno(rs.getString("clno"));
            rentinfo.setDiyaMoney(rs.getDouble("diyamoney"));
            rentinfo.setEno(rs.getString("eno"));
            rentinfo.setOperator(rs.getString("operator"));
            rentinfo.setPayAllMoney(rs.getDouble("payallmoney"));
            rentinfo.setRentBeginDate(rs.getDate("rentbengindate"));
            rentinfo.setRentno(rs.getString("rentno"));
        }
         
        return rentinfo;
         
    }
     
    /**
     * 还车时,给订单写入租金总额
     * @param rentno
     * @param allMoney
     * @throws Exception
     */
    public void writeAllPayMoney(String rentno,double allMoney) throws Exception{
        String sql = "update trentinfo set payallmoney=? where rentno=?";
        this.openConnection();
        PreparedStatement ps = this.conn.prepareStatement(sql);
        ps.setDouble(1, allMoney);
        ps.setString(2, rentno);
        ps.executeUpdate();
        ps.close();
    }
     
    /**
     * 添加还车信息
     * @param rentno
     * @throws Exception
     */
    public void addRentBackInfo(String rentno) throws Exception{
        String sql = "insert into trentback values(?,?)";
        this.openConnection();
        PreparedStatement ps = this.conn.prepareStatement(sql);
        ps.setString(1, rentno);
        ps.setTimestamp(2,new java.sql.Timestamp(new Date().getTime()));
        ps.executeUpdate();
        ps.close();
    }
     
    /**
     * 还车时,设置所有订单中的车辆状态为'01'
     * @param rentno
     * @throws Exception
     */
    public void setRentBackMotoState(String rentno) throws Exception{
         
        String sql = "select d.mno  from trentinfo r,trentdetail d where r.rentno = d.rentno and r.rentno=?";
        this.openConnection();
        PreparedStatement ps = this.conn.prepareStatement(sql);
        ps.setString(1,rentno);
        ResultSet rs = ps.executeQuery();
        while(rs.next()){
            String mno = rs.getString("mno");
            updateRentBackMotoState(mno);
        }
        rs.close();
        ps.close();      
    }
     
    public void updateRentBackMotoState(String mno) throws Exception{
        String sql = "update tmoto set state='01' where mno=?";
        this.openConnection();
        PreparedStatement ps = this.conn.prepareStatement(sql);
        ps.setString(1, mno);
        ps.executeUpdate();        
        ps.close();
    }
     
    /**
     * 添加客户数据
     * @param client
     * @throws Exception
     */
    public void addTClient(TClient client) throws Exception{
        //查询客户是否存在
        boolean bRet = isHaveClient(client.getTel());
        if(!bRet){
            //添加客户数据   
            String sql = "insert into tclient values(?,?,?,?,?)";
            this.openConnection();
            PreparedStatement ps = this.conn.prepareStatement(sql);
            ps.setString(1, client.getTel());
            ps.setString(2, client.getCname());
            ps.setString(3, client.getCid());
            ps.setString(4, client.getTel());
            ps.setString(5, client.getAddress());
            ps.executeUpdate();
            ps.close();
        }      
    }
     
    /**
     * 通过手机号,查询指定用户是否存在
     * @param tel
     * @return
     */
    private boolean isHaveClient(String tel) throws Exception{
        boolean bRet = false;
         
        String sql = "select * from tclient where tel=?";
        this.openConnection();
        PreparedStatement ps = this.conn.prepareStatement(sql);
        ps.setString(1, tel);
        ResultSet rs = ps.executeQuery();
        while(rs.next()){
            bRet = true;
        }
        rs.close();
        ps.close();
         
        return bRet;
    }
     
    /**
     * 汽车租赁
     * @param rentinfo 
     * @return     返回订单号
     * @throws Exception
     */
    public String addRentInfo(TRentInfo rentinfo) throws Exception{
        String sql = "insert into trentinfo values(?,?,?,?,?,?,?)";
         
        this.openConnection();
        PreparedStatement ps = this.conn.prepareStatement(sql);
        SimpleDateFormat sd = new SimpleDateFormat("yyyyMMdd");
        String rentno = "rno-" + sd.format(new Date()) + "-" + (new Date()).getTime();
        ps.setString(1, rentno);
        ps.setString(2, rentinfo.getClno());
        ps.setString(3, rentinfo.getEno());
        ps.setString(4, rentinfo.getOperator());
        ps.setDate(5new java.sql.Date(rentinfo.getRentBeginDate().getTime()));
        ps.setDouble(6, rentinfo.getDiyaMoney());
        ps.setDouble(7, rentinfo.getPayAllMoney());
        ps.executeUpdate();
        ps.close();
         
        return rentno;
    }
     
    /**
     * 添加租赁明细
     * @param rentDetail
     * @throws Exception
     */
    public void addRentDetail(TRentDetail rentDetail) throws Exception{
        //添加明细
        String sql = "insert into TRentDetail values(seq_rentdetail_id.nextval,?,?,?)";
        this.openConnection();
        PreparedStatement ps = this.conn.prepareStatement(sql);
        ps.setString(1, rentDetail.getRentno());
        ps.setString(2, rentDetail.getMno());
        ps.setDouble(3,rentDetail.getDaymoney());
        ps.executeUpdate();
        ps.close();    
        //修改汽车状态值
        updateMotoStateByRent(rentDetail.getMno());
    }
     
    /**
     * 修改汽车状态值,从未出租改为出租中
     * @param moto
     * @throws Exception
     */
    private void updateMotoStateByRent(String mno) throws Exception{
       String sql = "update tmoto set state='02' where mno=? and state='01'";
       this.openConnection();
       PreparedStatement ps = this.conn.prepareStatement(sql);
       ps.setString(1, mno);
       int iRet = ps.executeUpdate();
       if(iRet==0){
           throw new MotoRentFailException( mno+ ",该车已被别人租用,请重新选择");
       }
       ps.close();
    }
     
    /**
     * 添加汽车单表数据
     * @param moto
     * @throws Exception
     */
    public void addMoto(Moto moto) throws Exception{
        String sql = "insert into tmoto values(?,?,?,?)";
        this.openConnection();
        PreparedStatement ps = this.conn.prepareStatement(sql);
        ps.setString(1,moto.getMno());
        ps.setString(2,moto.getMtype().getTno());
        ps.setInt(3, moto.getSeatCount());
        ps.setString(4, MotoState.PREPARED_RENT);
        ps.executeUpdate();
        ps.close();
    }
     
    /**
     * 添加卡车单表数据
     * @param truck
     * @throws Exception
     */
    public void addTruck(TruckEntity truck) throws Exception{
        String sql = "insert into truck values(?,?,?)";
        this.openConnection();
        PreparedStatement ps = this.conn.prepareStatement(sql);
        ps.setString(1, truck.getMno());
        ps.setInt(2, truck.getDun());
        ps.setDouble(3, truck.getPriceEachDun());
        ps.executeUpdate();
        ps.close();    
    }
 
 
}