SpringBoot项目--电脑商城【新增收货地址】

1.新增收货地址

t_address

sql 复制代码
CREATE TABLE t_address (
	aid INT AUTO_INCREMENT COMMENT '收货地址id',
	uid INT COMMENT '归属的用户id',
	`name` VARCHAR(20) COMMENT '收货人姓名',
	province_name VARCHAR(15) COMMENT '省-名称',
	province_code CHAR(6) COMMENT '省-行政代号',
	city_name VARCHAR(15) COMMENT '市-名称',
	city_code CHAR(6) COMMENT '市-行政代号',
	area_name VARCHAR(15) COMMENT '区-名称',
	area_code CHAR(6) COMMENT '区-行政代号',
	zip CHAR(6) COMMENT '邮政编码',
	address VARCHAR(50) COMMENT '详细地址',
	phone VARCHAR(20) COMMENT '手机',
	tel VARCHAR(20) COMMENT '固话',
	tag VARCHAR(6) COMMENT '标签',
	is_default INT COMMENT '是否默认:0-不默认,1-默认',
	created_user VARCHAR(20) COMMENT '创建人',
	created_time DATETIME COMMENT '创建时间',
	modified_user VARCHAR(20) COMMENT '修改人',
	modified_time DATETIME COMMENT '修改时间',
	PRIMARY KEY (aid)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

注意name是关键字,所以需要用``

2.创建收货地址的实体类

在entity包下创建实体类Address继承BaseEntity类

java 复制代码
/**
 * 收货地址的实体类
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address extends BaseEntity {
    private Integer aid;//收货地址id
    private Integer uid;//归属用户id
    private String name;//收货人姓名
    private String provinceName;//省
    private String provinceCode;//省行政代号
    private String cityName;//市名
    private String cityCode;//市行政代号
    private String areaName;//区名
    private String areaCode;//区行政代号
    private String zip;//邮政编码
    private String address;//详细地址
    private String phone;//手机
    private String tel;//固话
    private String tag;//标签
    private Integer isDefault;//是否默认  0-不默认,1-默认
}

3.持久层[Mapper]

1 各功能的开发顺序

当前收货地址功能模块:

  • 第一个页面:列表的展示,修改,删除,设置默认
  • 第二个页面:新增收货地址

开发顺序:新增收货地址->列表的展示->设置默认收货地址->删除收货地址->修改收货地址

2 需要规划要执行的sql语句

1.新增收货地址对应的是插入语句:

java 复制代码
insert into t_address (aid以外的所有字段) values (字段值)
  1. 大部分平台都会规定一个用户的收货地址数量,这里规定最多20个.那么在插入用户新的地址之前就要先做查询操作.如果查询到的是刚好20,这并不是一个java语法的异常,可以认为是业务控制的异常,这个异常随后在service抛,在controller捕获
java 复制代码
select count(*) from t_address where uid=?

2.设置接口和抽象方法

创建接口AddressMapper,在这个接口中定义上面两个SQL语句抽象方法定义

java 复制代码
//收货地址持久层接口
public interface AddressMapper {
    /**
     * 插入用户的收货地址数据
     *
     * @param address 收货地址
     * @return 受影响的行数
     */
    Integer insertAddress(Address address);

    /**
     * 获取收货地址的数量(不能超过20),根据用户的uid
     *
     * @param uid 用户uid
     * @return 收货地址数量
     */
    Integer countAddress(Integer uid);
}

3.编写映射

1.在mapper标签中配置Address类属性与数据库中表的字段映射

XML 复制代码
    <resultMap id="AddressEntityMap" type="com.example.mycomputerstore.entity.Address">
        <id     column="aid"            property="aid"/>
        <result column="province_code"  property="provinceCode"/>
        <result column="province_name"  property="provinceName"/>
        <result column="city_code"      property="cityCode"/>
        <result column="city_name"      property="cityName"/>
        <result column="area_code"      property="areaCode"/>
        <result column="area_name"      property="areaName"/>
        <result column="is_default"     property="isDefault"/>
        <result column="created_user"   property="createdUser"/>
        <result column="created_time"   property="createdTime"/>
        <result column="modified_user"  property="modifiedUser"/>
        <result column="modified_time"  property="modifiedTime"/>
    </resultMap>

判断该映射是否配置成功:按着ctrl并点击type="com.cy.store.entity.Address"中的Address,如果能跳转到Address类说明映射成功

2.在AddressMapper.xml中配置以上两个抽象方法的映射

java 复制代码
    <insert id = "insertAddress" useGeneratedKeys="true" keyProperty="aid">
        INSERT INTO t_address (
            uid, name, province_name, province_code, city_name, city_code, area_name, area_code, zip,
            address, phone, tel, tag, is_default, created_user, created_time, modified_user, modified_time)
        VALUES (
                   #{uid}, #{name}, #{provinceName}, #{provinceCode}, #{cityName}, #{cityCode},
                   #{areaName}, #{areaCode}, #{zip}, #{address}, #{phone}, #{tel}, #{tag},
                   #{isDefault}, #{createdUser},#{createdTime}, #{modifiedUser}, #{modifiedTime})
    </insert>

    <select id="countAddress" resultType="java.lang.Integer">
        SELECT count(*) FROM t_address WHERE uid = #{uid}
    </select>

4.单元测试

java 复制代码
@SpringBootTest
@RunWith(SpringRunner.class)
public class AddressMapperTests {

    @Autowired
    private AddressMapper addressMapper;

    @Test
    public void insert() {
        Address address = new Address();
        address.setUid(11);
        address.setPhone("133336");
        address.setName("女朋友");
        addressMapper.insert(address);
    }

    @Test
    public void countByUid() {
        Integer count = addressMapper.countByUid(11);
        System.out.println(count);
    }
}

4.业务层[service]

1. 规划异常

  • 插入数据时用户不存在(被管理员误删等等),抛UsernameNotFoundException异常(已经有了,不需要重复创建)
  • 当用户插入的地址是第一条时,需要将当前地址作为默认收货地址
  • 实现办法:如果查询到统计总数为0则将当前地址的is_default值设置为1
  • 如果查询的结果>=20,这时需要抛出业务控制的异常AddressCountLimitException
java 复制代码
/**收货地址总数超出限制的异常(20条)*/
public class AddressCountLimitException extends ServiceException {
    /**重写ServiceException的所有构造方法*/
}
  • 插入数据时产生未知的异常InsertException(已经有了,不需要重复创建)

2 设计接口和抽象方法及实现

1.创建一个IAddressService接口,在接口中定义业务的抽象方法

因为mapper层接口该功能模块定义了两个抽象方法,所以就要在service层接口该功能模块也定义两个抽象方法?不是这样的,要看mapper层的这两个方法是依赖关系还是独立关系,如果某一个抽象方法依赖于另一个抽象方法,那就需要在业务层将这两个方法整合到一个方法中.一句话来说就是:一个功能模块可能需要多条sql语句

java 复制代码
//收货地址业务层接口
public interface IAddressService {
    /**
     *这三个参数的由来:
     * 1.首先肯定要有address
     * 2.业务层需要根据uid查询该用户收货地址总数及新建地址时给字段uid赋值
     * 但新建收货地址的表单中并没有哪个控件让输入用户uid,所以需要控制层将uid传给业务层
     * 3.业务层在创建/修改收货地址时需要同时修改数据库中创建人/修改人的字段
     * 但新建收货地址的表单中并没有哪个控件让输入用户username,所以需要控制层将username传给业务层
     * 注意:> 可以用HttpSession session代替Integer uid, String username,但
     * 这样写的话就需要把BaseController类下获取uid,username的方法重新封装到一个
     * 类中并让IAddressServiceImp实现类继承该类,这样就需要微调一下代码逻辑,太麻
     * 烦,并且,最好每一层只处理该层需要做的事情,session对象是控制层传递的,所以就
     * 把session对象定义封装在控制层中,不需要在业务层中额外处理以降低耦合
     */
    void addAddress(Integer uid, String username, Address address);
}

方法addNewAddress中三个参数的由来:

  • 首先肯定要有address
  • 业务层需要根据uid查询该用户收货地址总数及新建地址时给字段uid赋值
  • 但新建收货地址的表单中并没有哪个控件让输入用户uid,所以需要控制层将uid传给业务层并在业务层封装到address对象中
  • 业务层在创建/修改收货地址时需要同时修改数据库中创建人/修改人的字段
  • 但新建收货地址的表单中并没有哪个控件让输入用户username,所以需要控制层将username传给业务层并在业务层封装到address对象中

可以用HttpSession session代替Integer uid, String username,但这样写的话就需要把BaseController类下获取uid,username的方法重新封装到一个类中并让AddressServiceImpl实现类继承该类,这样就需要微调一下代码逻辑,太麻烦,并且,最好每一层只处理该层需要做的事情,session对象是控制层传递的,所以就把session对象定义封装在控制层中,不需要在业务层中额外处理,这样可以降低耦合

2.创建一个AddressServiceImpl类实现接口中抽象方法

java 复制代码
@Service
public class IAddressServiceImpl implements IAddressService {

    @Autowired
    private AddressMapper addressMapper;


    //在添加用户的收货地址的业务层依赖于DistrictService的业务层接口
    @Autowired
    private IDistrictService districtService;

    /**
     * 为了方便日后修改最大收货地址数量,可以在配置文件
     * application.properties中定义user.address.max-count=20
     */
    @Value("${user.address.max-count}")
    private Integer MaxAddress ;


    @Override
    public void addAddress(Integer uid, String username, Address address) {
        //查询收货地址信息是否大于20
        Integer count = addressMapper.countAddress(uid);
        if(count >= MaxAddress) {
            throw new AddressCountLimitException("收货地址不能超过20条");
        }

        //设置信息
        address.setUid(uid);
        Integer isDelete = count == 0 ? 1 : 0;//1表示默认收货地址,0反之
        address.setIsDefault(isDelete);
        address.setCreatedTime(new Date());
        address.setCreatedUser(username);
        address.setModifiedTime(new Date());
        address.setModifiedUser(username);

        //对address对象中的数据进行补全:省市区
        String provinceName = districtService.getNameByCode(address.getProvinceCode());
        String cityName = districtService.getNameByCode(address.getCityCode());
        String areaName = districtService.getNameByCode(address.getAreaCode());
        address.setProvinceName(provinceName);
        address.setCityName(cityName);
        address.setAreaName(areaName);


        //插入收货地址
        Integer row = addressMapper.insertAddress(address);
        if(row != 1) {
            throw new InsertException("未知错误 在 新建收货地址");
        }
    }
}

别忘了在配置文件application.properties中定义user.address.max-count=20

3 单元测试

在test下的service文件夹下创建AddressServiceTests测试类

java 复制代码
@SpringBootTest
@RunWith(SpringRunner.class)
public class AddressServiceTests {
    @Autowired
    private IAddressService addressService;

    @Test
    public void addNewAddress() {
        Address address = new Address();
        address.setPhone("175726");
        address.setName("男朋友");
        addressService.addNewAddress(11,"mxy",address);
    }
}

5.控制层[Controller]

1 处理异常

义务层抛出了收货地址总数超出上限的异常,在BaseController中进行捕获处理

java 复制代码
else if (e instanceof AddressCountLimitException) {
    result.setState(4003);
    result.setMessage("用户的收货地址超出上限的异常");
}

2 设计请求

  • /addresses/add_new_address
  • post
  • Address address,HttpSession session
  • JsonResult<Void>

3. 处理请求

在controller包下创建AddressController并继承BaseController,该类用来处理用户收货地址的请求和响应

java 复制代码
/**
 * 用户收货地址
 */
@RestController
@RequestMapping("/address")
public class AddressController extends BaseController {

    @Autowired
    private IAddressService addressService;


    /**
     * 新增用户收货地址
     *
     * @param address
     * @param session:这里使用session 是为了获取用户的uid和username
     * @return
     */
    @PostMapping("/add_new_address")
    public JsonResult<Void> addAddress(Address address, HttpSession session) {
        //先获取用户uid和username
        Integer uid = getuidFromSession(session);
        String username = getUsernameFromSession(session);
        addressService.addAddress(uid, username, address);
        return new JsonResult<>(OK);
    }
}

6.前端页面

javascript 复制代码
		$("#btn-add-new-address").click(function (){
			$.ajax({
				url:"/address/add_new_address",
				type:"POST",
				data:$("#form-add-new-address").serialize(),
				dataType:"JSON",
				success(e){
					if(e.state==200){
						alert("新增收货地址成功")
					}else{
						alert("新增收货地址失败")
					}
				},
				error(xhr){
					alert("新增收货地址产生未知的异常"+xhr.status)
				}
			})
		})
相关推荐
学会沉淀。几秒前
Docker学习
java·开发语言·学习
如若1232 分钟前
对文件内的文件名生成目录,方便查阅
java·前端·python
PyAIGCMaster3 分钟前
文本模式下成功。ubuntu P104成功。
服务器·数据库·ubuntu
drebander16 分钟前
MySQL 查询优化案例分享
数据库·mysql
初晴~32 分钟前
【Redis分布式锁】高并发场景下秒杀业务的实现思路(集群模式)
java·数据库·redis·分布式·后端·spring·
盖世英雄酱5813637 分钟前
InnoDB 的页分裂和页合并
数据库·后端
黑胡子大叔的小屋1 小时前
基于springboot的海洋知识服务平台的设计与实现
java·spring boot·毕业设计
ThisIsClark1 小时前
【后端面试总结】深入解析进程和线程的区别
java·jvm·面试
计算机毕设孵化场2 小时前
计算机毕设-基于springboot的校园社交平台的设计与实现(附源码+lw+ppt+开题报告)
spring boot·课程设计·计算机毕设论文·计算机毕设ppt·计算机毕业设计选题推荐·计算机选题推荐·校园社交平台
雷神乐乐2 小时前
Spring学习(一)——Sping-XML
java·学习·spring