-一、需求分析

-二、概要设计

-三、详细设计

    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( 5 new  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();    
     }
 
 
}