酒旅项目(纪念第一次真项目)

一.项目功能描述

一个简单订购酒店的小程序。主要功能包括:首页(搜索页,选择城市页面,签到页面)查询结果列表(浏览历史/我的收藏页面)酒店详情页(酒店图片,问大家页面及子页面)预定确定页面(订单,我的订单,订单详情,订单搜索)

二.系统架构图

三.技术栈

SpringBoot:容器+MVC框架

SpringSecurity:认证和授权框架

MyBatis:ORM框架

Elasticsearch:搜索引擎

RabbitMQ:消息队列

Redis:分布式缓存

LogStash:日志收集工具

Kibina:日志可视化查看工具

Nginx:静态资源服务器

Docker:应用容器引擎

Jenkins:自动化部署工具

Druid:数据库连接池

OSS MinIO:对象存储

JWT:JWT登录支持

Lombok:简化对象封装工具

Hutool:Java工具类库

PageHelper:MyBatis物理分页插件

knife4j:文档生成工具

Hibernator-Validator:验证框架

四.项目实现

1.熟悉产品 流程梳理

时间流程图

软件开发流程图(从同期大哥那搬运的!!!抱拳)

2.数据库设计

本队数据库设计

        在开始设计数据库的时候 还是没有对负责的部分流程没有梳理清楚 开始只设计出来两三个表 对于项目功能了解不全面 再开会之后进行修改才慢慢了解如何制作合理数据库 根据阿里规范手册一遍一遍的修改自己设计的数据库 力求先将自己设计的设计规范没有错误 再力求和项目更贴合 最终我们队设计的数据库比我之前认知设计的好太多 队长做出的贡献很多 这也说明我在设计数据库方面还有很多不足 在后面学习还要复习数据库知识 编写按照规范手册进行编写 也让我了解到了数据库设计在整个项目中的作用 在设计数据库中主键,创建时间,更新时间,逻辑删除标识等重要的特殊的点 

  1. 【强制】在数据库中不能使用物理删除操作,要使用逻辑删除
  2. 【强制】表必备三字段:id,create_time,update_time
  3. 【强制】小数类型为 decimal,禁止使用 float 和 double
  4. 【强制】表名不使用复数名词

还有一直不理解的索引功能 它也有索引规约进行规范完善

  1. 【强制】业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引
  2. 【强制】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。
  3. 【强制】在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区 分度决定索引长度

等等让我更加理解数据库如何设计的更合理规范适合项目开发

3.接口设计

    设计接口方面从开始的只是略懂到现在的熟悉如何设计api收获满满 熟悉项目的功能及其基础服务进行api设计 还是因为第一次尝试不知何处入手 看着各个师兄设计的才有了点想法 着手设计了一两个才慢慢熟悉接口的开发 但了解了设计api接口该有的流程还是很受用 设计接口时使用的是ApiPost类似于postman 但它可以支持共享项目协作开发 这个工具更加方便 接口设计也有许多小点需要注意 将我个人容易犯错的地方例举几条

  1. GET:请求资源 POST:创建资源 PUT:修改资源所有数据 DELETE:删除资源 PATCH:修改资源部分数据 OPTION/HEAD:基本不用
  2. 金额类型/时间日期类型的属性值,如果仅用来显示,建议后端返回可以显示的字符串

  3. 属性值为空时,严格按类型返回默认值

  4. 注意使用名词的复数形式:表示资源的集合
  5. 采用JSON格式返回响应数据
  6. 短小便于输入 人可以读懂 没有大小写混用 方便修改 不会暴露服务架构 规则统一

4.代码开发及review

初代设计

开会选择了两个简单的接口进行编码 因为第一次进行真项目 对自己实力还是有底从简单开始慢慢来 根据课程编码方式采用mybaits的风格进行的简单编写

pojo层

package com.kkb.pojo;

import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;

import java.util.Date;

public class HotelAnswer {
    private Long id;

    private Long question_id;

    private Long user_id;

    private Byte user_identity;

    private String user_nickname;

    private String user_head_portrait;

    private String content;

    private Boolean deleted;
    @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date create_time;
    @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date update_time;
}
package com.kkb.pojo;

import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;

import java.util.Date;

public class HotelQuestion {
    private Long id;

    private Long hotel_id;

    private String hotel_name;

    private Long user_id;

    private String user_nickname;

    private String content;

    private Byte deleted;
    @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date create_time;
    @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date update_time;
}

service层

@Service
public class HotelAnswerService {

    @Resource
    private HotelAnswerMapper hotelAnswerMapper;

    /**
     * 添加回答
     * @param hotelAnswer
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = {Exception.class})
    public int addAnswer(HotelAnswer hotelAnswer){
        return hotelAnswerMapper.insertSelective(hotelAnswer);
    }
}
@Service
public class HotelQuestionService {
    @Resource
    private HotelQuestionMapper hotelQuestionMapper;

    /**
     * 添加问题
     * @param hotelQuestion
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = {Exception.class})
    public int addQuestion(HotelQuestion hotelQuestion){
        return hotelQuestionMapper.insertSelective(hotelQuestion);
    }
}

controller层

@Controller
@RequestMapping("http://82.157.123.73:{port}/hotel")
@ResponseBody
public class HotelAnswerController {

    @Resource
    private HotelAnswerService hotelAnswerService;

    @RequestMapping(value = "/answer/save",method = RequestMethod.POST)
    public ResultVO<HotelAnswer> addAnswer(HotelAnswer hotelAnswer){
        System.out.println(hotelAnswer);
        int i = hotelAnswerService.addAnswer(hotelAnswer);
        if (i == 1){
            return new ResultVO<HotelAnswer>();
        }
        return new ResultVO<>(500,"添加失败");
    }
}
@Controller
@RequestMapping("http://82.157.123.73:{port}/hotel")
@ResponseBody
public class HotelQuestionController {
    @Resource
    private HotelQuestionService hotelQuestionService;

    @RequestMapping(value = "/answer/save",method = RequestMethod.POST)
    public ResultVO<HotelQuestion> addQuestion(HotelQuestion hotelQuestion){
        int i = hotelQuestionService.addQuestion(hotelQuestion);
        if (i == 1){
            return new ResultVO<HotelQuestion>();
        }
        return new  ResultVO<>(500,"添加失败");
    }
}

只编写了酒店问答部分的新增两个接口 根据课程学习的编写觉得不是很难 但开会的时候看其他接口的编写觉得自己还只会简单的crud 例如涉及到有关经纬度 多表联查等部分就是没有很懂提点之后也是茫茫然然 这也是以后学习中要慢慢学习的地方 开会中提到编码应该改正的地方

  1. 设置非空验证
  2. 一些配置不要放在业务层
  3. if else问题  可以直接if return让代码简洁

  4. 不用try catch  使用全局捕捉异常例GlobalExceptionHandler、ApiException直接引common模块使用

  5. 日志打印问题 在主要流程打印日志,节约资源

  6. 接口注释写好增加可读性

  7. if else特别多时用switch语句,让代码干净整洁些

  8. 使用mybatis-plus 例如Service直接继承Iservice的方法简洁代码

  9. minio上传文件单独写一个service

  10. 大小写问题例如DTO、VO

  11. 字符串拼接问题

  12. 非空用hutool工具

经过开会进行代码修改和指正我的两个接口也大修改了一下 根据我们队的数据库进行了修改 但因为和其他队的数据库以及接口出现了相交的地方 两队合二为一进行以一队为基础的编写 虽然走了些弯路但还是值得的 最终也借鉴了他人的代码进行了调整 没有使用xml这种形式是代码简介度更高

最终设计

domain实体类(将问答表合二为一更加方便)

@Data
@TableName("ask_answer_info")
@Accessors(chain = true)
public class AskAnswerInfo implements Serializable {

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    @ApiModelProperty(value = "用户信息id", required = true)
    private Long userInfoId;

    @ApiModelProperty(value = "用户名称", required = true)
    private String username;

    @ApiModelProperty(value = "酒店id", required = true)
    private Long hotelInfoId;

    @ApiModelProperty(value = "内容", required = true)
    private String content;

    @ApiModelProperty(value = "是否过审 1->过审 0->不过审", required = true)
    private Integer published;

    @ApiModelProperty(value = "0->不删除 1->删除", required = true)
    private Integer deleted;

    @ApiModelProperty(value = "父id 用于 评论与问题匹配", required = true)
    private Long parentId;

    @ApiModelProperty(value = "种类 2->问题 1->回答", required = true)
    private Integer type;

    @ApiModelProperty(value = "酒店名称", required = true)
    private String hotelName;

    @ApiModelProperty(value = "回复数量", required = true)
    private Integer replyCount;

    @ApiModelProperty(value = "创建时间", required = true)
    private Date createTime;

    @ApiModelProperty(value = "更新时间", required = true)
    private Date updateTime;

    @TableField(exist = false)
    private List<AskAnswerInfo> childAnswer= new ArrayList<>();

}

service层 

public interface IAskAnswerInfoService extends IService<AskAnswerInfo> {

    /**
     * 保存问题
     *
     * @param askEveryoneAnswer 要保存的问题对象
     */
    void saveQuestion(AskAnswerInfo askEveryoneAnswer);

    /**
     * 保存回答
     *
     * @param askEveryoneAnswer 要保存的回答对象
     */
    void saveAnswer(AskAnswerInfo askEveryoneAnswer);

}
@Service
public class AskAnswerInfoServiceImpl extends ServiceImpl<AskAnswerInfoDAO, AskAnswerInfo> implements IAskAnswerInfoService {

    @Autowired(required = false)
    private InviteAnswerServiceImpl inviteAnswerService;

    /**
     * 保存问题
     *
     * @param askEveryoneAnswer 要保存的问题对象
     */
    @Override
    public void saveQuestion(AskAnswerInfo askEveryoneAnswer) {
        //先判断是否过审  假设过审后
        askEveryoneAnswer.setPublished(1);
        boolean result = this.saveOrUpdate(askEveryoneAnswer);
        if (!result) {
            Asserts.fail("保存失败");
        }
        System.out.println(askEveryoneAnswer);
        System.out.println(orderInfoClient.findRandInviteUserInfoId(askEveryoneAnswer.getHotelInfoId()));
        List<Long> randIdByHotelId = orderInfoClient.findRandInviteUserInfoId(askEveryoneAnswer.getHotelInfoId());
        System.out.println(randIdByHotelId);
        if (CollUtil.isEmpty(randIdByHotelId)){
            return;
        }
        List<InviteAnswer> collect = randIdByHotelId.stream().map(aLong -> new InviteAnswer()
                .setQuestionUserId(askEveryoneAnswer.getUserInfoId())
                .setHotelInfoId(askEveryoneAnswer.getHotelInfoId())
                .setAnswerUserId(aLong)
                .setAskAnswerInfoId(askEveryoneAnswer.getId())).collect(Collectors.toList());
        boolean b = inviteAnswerService.saveBatch(collect);
        if (!b) {
            Asserts.fail("邀请失败");
        }
    }

    /**
     * 保存回答
     *
     * @param askEveryoneAnswer 要保存的回答对象
     */
    @Override
    public void saveAnswer(AskAnswerInfo askEveryoneAnswer) {
        //先判断是否过审  假设过审后
        askEveryoneAnswer.setPublished(1);
        //保存answer
        boolean result = this.saveOrUpdate(askEveryoneAnswer);
        if (!result) {
            Asserts.fail
                    ("保存失败");
        }
        AskAnswerInfo askEveryoneAnswerParent =
                this.getById(askEveryoneAnswer.getParentId());
        Integer replyCount = askEveryoneAnswerParent.getReplyCount();
        askEveryoneAnswerParent.setReplyCount(replyCount + 1);
        this.updateById(askEveryoneAnswerParent);
        inviteAnswerService.dealQuestion(askEveryoneAnswer.getUserInfoId(), askEveryoneAnswer.getParentId());
    }

}

 controller层

@RestController
@RequestMapping("/user/ask-question")
public class AskAnswerInfoController {

    @Autowired
    private AskAnswerInfoServiceImpl askEveryoneAnswerService;

    //合并 type
    @RequestMapping(value = "/my-question", method = RequestMethod.GET)
    @ApiOperation(value = "获取某个用户的全部问题", notes = "获取某个用户的全部问题")
    public CommonResult<List<AskAnswerInfo>> getMyQuestion( @NotNull Long id) {
        List<AskAnswerInfo> myQuestion = askEveryoneAnswerService.getMyQuestion(id);
        return CommonResult.success(myQuestion);
    }

    @RequestMapping(value = "/my-answer", method = RequestMethod.GET)
    @ApiOperation(value = "获取某个用户的全部回答", notes = "获取某个用户的全部回答")
    public CommonResult<List<AskAnswerInfo>> getMyAnswer(@NotNull Long id) {
        List<AskAnswerInfo> myAnswer = askEveryoneAnswerService.getMyAnswer(id);
        return CommonResult.success(myAnswer);
    }

    @RequestMapping(value = "/hotel-question-all-list", method = RequestMethod.GET)
    @ApiOperation(value = "获取某个就酒店全部问答", notes = "获取某个就酒店全部问答")
    public CommonResult<List<AskAnswerInfo>> getHotelQuestionAllList(@NotNull Long hotelId) {
        List<AskAnswerInfo> hotelQuestionAllList = askEveryoneAnswerService.getHotelQuestionAllList(hotelId);
        return CommonResult.success(hotelQuestionAllList);
    }

    @RequestMapping(value = "/question-all-one", method = RequestMethod.GET)
    @ApiOperation(value = "获得某一个问答具体内容", notes = "获得某一个问答具体内容")
    public CommonResult<AskAnswerInfo> getQuestionAllOne( @NotNull Long questionId) {
        AskAnswerInfo questionAllOne = askEveryoneAnswerService.getQuestionAllOne(questionId);
        return CommonResult.success(questionAllOne);
    }

    //以下四个方法可以合并,在service层做类型判断
    @RequestMapping(value = "/question/save", method = RequestMethod.POST)
    @ApiOperation(value = "保存问题", notes = "保存问题")
    public CommonResult saveQuestion(@NotNull AskAnswerInfo askEveryoneAnswer) {
        askEveryoneAnswerService.saveQuestion(askEveryoneAnswer);
        return CommonResult.success("保存成功");
    }

    @RequestMapping(value = "/answer/save", method = RequestMethod.POST)
    @ApiOperation(value = "保存回答", notes = "回答")
    public CommonResult saveAnswer(@NotNull AskAnswerInfo askEveryoneAnswer) {
        askEveryoneAnswerService.saveAnswer(askEveryoneAnswer);
        return CommonResult.success("保存成功");
    }

    @RequestMapping(value = "/question/delete", method = RequestMethod.GET)
    @ApiOperation(value = "删除问题", notes = "删除问题")
    public CommonResult deletedQuestion(@NotNull Long questionId) {
        askEveryoneAnswerService.deletedQuestion(questionId);
        return CommonResult.success("删除成功");
    }

    @RequestMapping(value = "/answer/delete", method = RequestMethod.GET)
    @ApiOperation(value = "删除答案", notes = "删除答案")
    public CommonResult deletedAnswer(@RequestParam("answerId") @NotNull Long answerId
            , @RequestParam("questionId") Long questionId) {
        askEveryoneAnswerService.deletedAnswer(answerId, questionId);
        return CommonResult.success("删除成功");
    }

}

这个版本是最终一队版本 比我写的要好 所以直接展示我负责的部分 修改过后的代码确实与项目流程情况更加符合更贴切 与初代版本不论是数据库和接口以及代码的编写都有很大改观和细化 成功完成项目所需流程

五.项目总结

这是我第一次的真项目 短短一个月学习到很多 对于Java编程也有更深理解 从0到1 从项目流程梳理到项目部署上线 都真真实实的发生了 学习到了一个项目的从无到有 一步步走来自己的编码技术也提升了不少 但对于一些大佬可能还是小意思 这个行业的每个人都是这样过来的 项目而开始我没有参加框架部署 对于自己的实力很了解 所以这也是以后要努力学习的地方 还有项目的框架图以及这个项目设计的各个技术 对于小白的我来说还是没有吃透 这也是这个项目给我带来的好处 会反反复复熟悉项目 学习别人的编码方式转化为自己的东西 了解架构图底层知识点 之后也要准备准备面试题开始死背 算了慢慢背哈哈哈哈哈 按自己计划进行 感谢这次项目学习到很多    That's All Over !

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值