1. 项目需求
中国移动,中国联通,中国电信是国内3大通信运营商,每个运营商都提供了不同的品牌套餐来应对不同的用户群,比如北京移动主要有全球通,神州行,动感地带等3大品牌套餐,每种套餐的内容和费用不同,嗖嗖移动是一个假定的通信运营商,提供了话痨套餐,网虫套餐,超人套餐,各种套餐所包含的服务内容及费用如下表:
如实际使用中超出套餐内包含的通话时长,短信条数和上网流量,则按一下规则计费:
* 超出的通话: 0.2元/分
* 超出的短信:0.1元/条
* 超出的上网流量:0.1元/MB
本任务实现的"嗖嗖移动业务大厅"提供了嗖嗖移动用户的常用功能,包括新用户注册,本月账单查询,套餐余量查询,打印消费详情,套餐变更,办理退网,话费充值,查看消费记录,查看话费说明等功能.另外,还可以模拟用户通话,上网,发送短信的场景进行相应的扣费并记录消费信息.各功能介绍如下表:
2. 项目使用的技术
面向对象的思想
封装,继承,多态,接口的使用
异常处理的合理使用
集合框架的使用
MySQL数据
JDBC操作数据库
dao开发模式
数据访问对象(DAO):DAO是一个接口或抽象类,定义了对数据的基本操作方法,如增加、删除、修改、查询等。 数据访问对象实现类(DAO实现类):DAO实现类是对DAO接口的具体实现,负责实现具体的数据访问逻辑 实体对象(Entity):实体对象是业务逻辑层与数据访问层之间的数据传输对象。它通常对应于数据存储中的一张表或一个文档,包含了与业务相关的数据和属性。
数据访问对象(DAO):DAO是一个接口或抽象类,定义了对数据的基本操作方法,如增加、删除、修改、查询等。
数据访问对象实现类(DAO实现类):DAO实现类是对DAO接口的具体实现,负责实现具体的数据访问逻辑
实体对象(Entity):实体对象是业务逻辑层与数据访问层之间的数据传输对象。它通常对应于数据存储中的一张表或一个文档,包含了与业务相关的数据和属性。
queryRunner
package com.fs.soso.dao.impl;
import com.fs.soso.dao.CradDao;
import com.fs.soso.entity.Crad;
import com.fs.soso.utils.JDBCUtil;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* @author hua
* @version 1.0
* @title CradDaoImpl
* @description 电话号码Dao实现类
* @create 2024/1/5 16:53
*/
public class CradDaoImpl implements CradDao {
@Override
public List<Crad> queryAll() {
Connection conn = null;
try {
conn = JDBCUtil.getConnection();
QueryRunner queryRunner=new QueryRunner();
String sql ="select * from tb_card";
List<Crad> crads = queryRunner.query(conn,sql,new BeanListHandler<Crad>(Crad.class));
return crads;
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtil.close(conn);
}
return null;
}
@Override
public Crad queryById(Integer id) {
Connection conn = null;
try {
conn = JDBCUtil.getConnection();
QueryRunner queryRunner=new QueryRunner();
String sql ="select * from tb_card where id = ?";
Crad crad = queryRunner.query(conn,sql,new BeanHandler<Crad>(Crad.class),id);
return crad;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
@Override
public int insert(Crad crad) {
Connection conn = null;
try {
conn = JDBCUtil.getConnection();
QueryRunner queryRunner=new QueryRunner();
String sql ="insert into tb_card values(null,?,?)";
return queryRunner.update(conn,sql,crad.getCardNumber(),crad.getStatus());
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return 0;
}
@Override
public int updateByID(Crad crad) {
Connection conn = null;
try {
conn = JDBCUtil.getConnection();
QueryRunner queryRunner=new QueryRunner();
String sql ="update tb_card set cardNumber=?, status=? where id=?";
return queryRunner.update(conn,sql,crad.getCardNumber(),crad.getStatus(),crad.getId());
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtil.close(conn);
}
return 0;
}
@Override
public int deleteById(Integer id) {
Connection conn = null;
try {
conn = JDBCUtil.getConnection();
String sql ="delete from tb_card where id=?";
QueryRunner queryRunner=new QueryRunner();
return queryRunner.update(conn,sql,id);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return 0;
}
@Override
public int updateByCardNumber(String cardNumber) {
Connection conn = null;
try {
conn = JDBCUtil.getConnection();
QueryRunner queryRunner=new QueryRunner();
String sql ="update tb_card set status=0 where cardNumber=?";
return queryRunner.update(conn,sql,cardNumber);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtil.close(conn);
}
return 0;
}
}
QueryRunner在执行SQL操作后会自动关闭相关的资源,如Connection、Statement和ResultSet等。这样可以避免手动管理资源的繁琐工作,同时也减少了资源泄漏的风险。
ueryRunner提供了一组简化的API,使得执行SQL查询和更新操作变得更加简单。它封装了JDBC的底层细节,隐藏了大量的冗余代码,使得开发人员可以更专注于业务逻辑的实现。
Junit测试
package com.fs.soso.dao;
import com.fs.soso.constant.SystemConstant;
import com.fs.soso.dao.impl.CradDaoImpl;
import com.fs.soso.entity.Crad;
import org.junit.Test;
import java.util.*;
import static org.junit.Assert.*;
/**
* @author hua
* @version 1.0
* @title CradDaoTest
* @description
* @create 2024/1/8 10:11
*/
public class CradDaoTest implements SystemConstant {
CradDao cradDao=new CradDaoImpl();
@Test
public void queryAll() {
List<Crad> crads=cradDao.queryAll();
for (Crad crad:crads) {
System.out.println(crad);
}
}
@Test
public void queryById() {
Crad crad =cradDao.queryById(4);
System.out.println(crad);
}
@Test
public void insert() {
Crad crad=new Crad("12345678910",SystemConstant.CRAD_NO_USED);
int result = cradDao.insert(crad);
System.out.println(result);
}
@Test
public void updateByID() {
Crad crad=new Crad("12345678910",SystemConstant.CRAD_USED);
int result = cradDao.updateByID(crad);
System.out.println(result);
}
@Test
public void deleteById() {
System.out.println((int)Math.random());
}
}
简单易用:JUnit提供了一组简单易用的API,使得编写和运行单元测试变得更加容易。开发人员只需要编写一些简单的测试方法,就可以快速测试代码的正确性。
注解支持:JUnit支持使用注解来标记测试方法和测试类。开发人员可以使用@Test注解标记测试方法
自动化测试:JUnit可以自动运行测试方法,并生成测试报告。开发人员只需要编写测试方法,JUnit会自动运行这些方法,并输出测试结果和错误信息。
3.项目成果展示
实体类
package com.fs.soso.entity;
import java.util.Date;
/**
* @author hua
* @version 1.0
* @title ConsumInfo
* @description 消费信息类
* @create 2024/1/5 16:06
*/
public class ConsumInfo extends Base{
private String cardNumber;
private String type;
private Integer consumData;
private Date consumeDate;
public ConsumInfo() {
}
public ConsumInfo(String cardNumber, String type, Integer consumData, Date consumeDate) {
this.cardNumber = cardNumber;
this.type = type;
this.consumData = consumData;
this.consumeDate = consumeDate;
}
public String getCardNumber() {
return cardNumber;
}
public void setCardNumber(String cardNumber) {
this.cardNumber = cardNumber;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Integer getConsumData() {
return consumData;
}
public void setConsumData(Integer consumData) {
this.consumData = consumData;
}
public Date getConsumeDate() {
return consumeDate;
}
public void setConsumeDate(Date consumeDate) {
this.consumeDate = consumeDate;
}
@Override
public String toString() {
return "ConsumInfo{" +
"id=" + getId() +
", cardNumber='" + cardNumber + '\'' +
", type='" + type + '\'' +
", consumData=" + consumData +
", consumeDate=" + consumeDate +
'}';
}
}
dao层
package com.fs.soso.dao;
import com.fs.soso.entity.ConsumInfo;
import java.util.List;
/**
* @author hua
* @version 1.0
* @title BaseDao
* @description
* @create 2024/1/9 10:31
*/
public interface BaseDao <T>{
List<T> queryAll();
/**
* 根据主键查询
* @param id
* @return
*/
T queryById(Integer id);
/**
* 添加
* @param t
* @return
*/
int insert(T t);
/**
* 修改
* @param t
* @return
*/
int updateByID(T t);
/**
* 根据主键删除
* @param id
* @return
*/
int deleteById(Integer id);
}
package com.fs.soso.dao;
import com.fs.soso.entity.ConsumInfo;
import java.util.Date;
import java.util.List;
/**
* @author hua
* @version 1.0
* @title ConsumInfoDao
* @description 消费信息DAO类
* @create 2024/1/5 16:38
*/
public interface ConsumInfoDao extends BaseDao<ConsumInfo>{
List<ConsumInfo> queryByConsumeDate(String cardNumber,Date consumeDate);
int deleteByCardNumber(String cardNumber);
}
dao实现类
package com.fs.soso.dao.impl;
import com.fs.soso.dao.ConsumInfoDao;
import com.fs.soso.entity.ConsumInfo;
import com.fs.soso.utils.DateUtil;
import com.fs.soso.utils.JDBCUtil;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Date;
/**
* @author hua
* @version 1.0
* @title ConsumInfoDaoImpl
* @description 消费信息DAO实现类
* @create 2024/1/5 16:52
*/
public class ConsumInfoDaoImpl implements ConsumInfoDao {
@Override
public List<ConsumInfo> queryAll() {
return null;
}
@Override
public ConsumInfo queryById(Integer id) {
return null;
}
@Override
public int insert(ConsumInfo consumInfo) {
Connection conn = null;
try {
conn = JDBCUtil.getConnection();
QueryRunner queryRunner=new QueryRunner();
String date = DateUtil.getDate(new Date());
String sql ="insert into tb_consuminfo values(null,?,?,?,?)";
return queryRunner.update(conn,sql,consumInfo.getCardNumber(),
consumInfo.getType(),consumInfo.getConsumData(),
date);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtil.close(conn);
}
return 0;
}
@Override
public int updateByID(ConsumInfo consumInfo) {
Connection conn = null;
QueryRunner queryRunner=new QueryRunner();
try {
conn = JDBCUtil.getConnection();
String sql ="update tb_consuminfo set card_number=?,type=?,consum_data=?,consume_date=? where id=?";
return queryRunner.update(sql,consumInfo.getCardNumber(),
consumInfo.getType(),consumInfo.getConsumData(),
consumInfo.getConsumeDate(),consumInfo.getId());
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtil.close(conn);
}
return 0;
}
@Override
public int deleteById(Integer id) {
Connection conn = null;
PreparedStatement pstm = null;
try {
conn = JDBCUtil.getConnection();
String sql ="delete from tb_consuminfo where id=?";
pstm = conn.prepareStatement(sql);
pstm.setObject(1,id);
return pstm.executeUpdate();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return 0;
}
@Override
public List<ConsumInfo> queryByConsumeDate(String cardNumber,Date consumeDate) {
Connection conn = null;
try {
conn = JDBCUtil.getConnection();
QueryRunner queryRunner=new QueryRunner();
String sql ="select id,card_number cardNumber,type," +
"consum_data consumData,consume_date consumeDate from tb_consuminfo " +
"where card_number=? and YEAR(consume_date) = ? and MONTH(consume_date)=?";
return queryRunner.query(conn,sql,new BeanListHandler<ConsumInfo>(ConsumInfo.class),
cardNumber,1900+consumeDate.getYear(),consumeDate.getMonth()+1);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtil.close(conn);
}
return null;
}
@Override
public int deleteByCardNumber(String cardNumber) {
Connection conn = null;
try {
conn = JDBCUtil.getConnection();
QueryRunner queryRunner = new QueryRunner();
String sql ="delete from tb_consuminfo where card_number=?";
return queryRunner.update(conn,sql,cardNumber);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
JDBCUtil.close(conn);
}
return 0;
}
}
嗖嗖服务类
/**
* 办理退网
* @param moboleCard 嗖嗖移动卡
*/
public static void outMoboleCard(MoboleCard moboleCard){
if(moboleCard.getMoney()<0){
System.out.println("你有欠款"+moboleCard.getMoney()+",请充值后再退网");
}else {
// 删除id对应的嗖嗖移动卡对象
MoboleCardDao moboleCardDao = new MoboleCardDaoImpl();
moboleCardDao.deleteById(moboleCard.getId());
// 解冻卡号对应的卡
CradDao cradDao = new CradDaoImpl();
cradDao.updateByCardNumber(moboleCard.getCardNumber());
// 删除卡号对应的月账单
MonthlyConsumptionRecordsDao monthlyConsumptionRecordsDao = new MonthlyConsumptionRecordsDaoImpl();
monthlyConsumptionRecordsDao.deleteByCardNumber(moboleCard.getCardNumber());
// 删除卡号对应的充值记录
RechargeRecordDao rechargeRecordDao = new RechargeRecordDaoImpl();
rechargeRecordDao.deleteByCardNumber(moboleCard.getCardNumber());
// 删除卡号对应的消费记录
ConsumInfoDao consumInfoDao = new ConsumInfoDaoImpl();
consumInfoDao.deleteByCardNumber(moboleCard.getCardNumber());
// 删除卡号对应的充值流量包记录
FlowRechargeRecordDao flowRechargeRecordDao = new FlowRechargeRecordDaoImpl();
flowRechargeRecordDao.deleteByCardNumber(moboleCard.getCardNumber());
System.out.println("[友情提示] :卡号" + moboleCard.getCardNumber() + "办理退网成功! ");
}
}
4.创新点展示
/**
* 忘记密码
*/
public static void forgetPassword(){
System.out.println("*******忘记密码*******");
System.out.println("请输入卡号:");
String cardNumber = scanner.next();
// 查询卡号对应的嗖嗖移动卡对象
MoboleCardDao moboleCardDao = new MoboleCardDaoImpl();
MoboleCard moboleCard = moboleCardDao.queryByCardNumber(cardNumber);
if (moboleCard == null) {
System.out.println("请输入正确的卡号");
}else if (moboleCard.getStatus() == SystemConstant.MOBOLE_STATUS_FREEZE) {
System.out.println("该号码已被冻结,请联系管理员");
}else {
System.out.println("请输入密码");
String password = scanner.next();
moboleCard.setPassword(password);
moboleCardDao.updateByID(moboleCard);
System.out.println("密码修改成功,请重新登录");
}
}
/**
* 申请解冻
*/
public static void thaw(){
System.out.println("*******申请解冻*******");
System.out.println("请输入卡号");
String cardNumber = scanner.next();
// 查询卡号对应的嗖嗖移动卡对象
MoboleCardDao moboleCardDao = new MoboleCardDaoImpl();
MoboleCard moboleCard = moboleCardDao.queryByCardNumber(cardNumber);
if (moboleCard == null) {
System.out.println("请输入正确的卡号");
} else {
if (moboleCard.getMoney()<0){
//解冻前不能欠款
System.out.println("您有欠款"+moboleCard.getMoney()+"元,请充值后再解冻");
}else if(moboleCard.getStatus()==SystemConstant.MOBOLE_STATUS_NORMAL){
//嗖嗖移动卡对象为正常
System.out.println("您无需解冻");
}else {
//解冻嗖嗖移动卡对象
moboleCard.setStatus(SystemConstant.MOBOLE_STATUS_NORMAL);
moboleCardDao.updateByID(moboleCard);
System.out.println("卡号"+moboleCard.getCardNumber()+"已解冻");
}
}
}
/**
* 购买流量
* @param moboleCard 嗖嗖移动卡
*/
public static void addFlow(MoboleCard moboleCard){
System.out.println("序号\t套餐名称\t\t上网流量\t价格\t\t使用期限");
// 查询所有流量包
FlowPackageDao flowPackageDao=new FlowPackageDaoImpl();
List<FlowPackage> flowPackages = flowPackageDao.queryAll();
for (FlowPackage flowPackage:flowPackages) {
System.out.println(flowPackage.getId()+"\t"+flowPackage.getName()
+"\t"+flowPackage.getData()/1024+"GB\t\t"+flowPackage.getMoney()
+"元\t"+flowPackage.getUseDay()+"天");
}
System.out.println("请选择套餐");
int id=scanner.nextInt();
FlowPackage flowPackage=new FlowPackage();
List<FlowPackage> collect = flowPackages.stream()
.filter(f -> f.getId() == id)
.collect(Collectors.toList());
if (collect.isEmpty()){
System.out.println("没有该套餐");
}else {
flowPackage=collect.get(0);
}
// 插入一条流量包购买记录
FlowRechargeRecordDao flowRechargeRecordDao=new FlowRechargeRecordDaoImpl();
DateUtil.getPastDueDate(new Date(),flowPackage.getUseDay());
FlowRechargeRecord flowRechargeRecord=new FlowRechargeRecord(
flowPackage.getData(),new Date(),
DateUtil.getPastDueDate(new Date(),flowPackage.getUseDay()),
moboleCard.getCardNumber());
flowRechargeRecordDao.insert(flowRechargeRecord);
System.out.println("您已购买"+flowPackage.getName());
// 插入一条消费记录
ConsumInfoDao consumInfoDao=new ConsumInfoDaoImpl();
ConsumInfo consumInfo =new ConsumInfo(moboleCard.getCardNumber(),"充值流量包",flowPackage.getData(),new Date());
consumInfoDao.insert(consumInfo);
// 扣除嗖嗖移动卡余额
moboleCard.setMoney(moboleCard.getMoney()-flowPackage.getMoney());
MoboleCardDao moboleCardDao=new MoboleCardDaoImpl();
moboleCardDao.updateByCardNumber(moboleCard,moboleCard.getCardNumber());
}
6.总结
亮点之处
采用了queryRunner,封装了JDBC的底层细节,隐藏了大量的冗余代码,自动关闭相关的资源,避免手动管理资源的繁琐工作
采用了Junit测试,使得编写和运行单元测试变得更加容易,保证了dao层代码的准确性
在原有的基础上添加了大量创新,使项目更加具有独特型,不可复制性
不足之处
不是很记得使用Java的三大要素,分明有重复的的代码,却不能记得自己封装。
总是不写注释,导致在修改的时候,要分析半天
soso服务类代码有点冗余,逻辑比较复杂,但我也不敢改