1.添加线索跟进记录 🚩
分析需求
注意,由于需求我们了解,如果客户修改了线索的信息,那么线索对应的数据也要跟着修改
确定页面位置
通过阅读接口文档,了解到信息
- 方法名:
/clues/record
- 请求方式:
POST
- 参数列表:
- 传入参数:
{
"clueId":9009, 线索id
"subject;":"1", 学科
"record":"根据记录", 根据记录
"level":"1", 意向级别
"type":"0", 0:正常跟进 1伪线索
"falseReason":"空号", 标记失败原因
"name":"张三", 姓名
"sex":"女", 性别
"weixin":"wx123456", 微信
"qq":"qq123456", QQ
"age":20 年龄
}
- 返回值:
{"msg":"操作成功","code":200}
步骤
- 根据请求的路径确定编写代码的位置/clues下,对应的就是找/clues这个一级窄化请求对应的controller
- 我们在idea里全局搜索/clues
- 没有找到,没有找到怎么办❓--自己建一个Controller
- 问题:在哪创建controller❓,所有的controller都要写在admin工程里
- 由于是线索跟进,所以在clues下创建一个controller
- 问题:在哪创建controller❓,所有的controller都要写在admin工程里
- 没有找到,没有找到怎么办❓--自己建一个Controller
- 我们在idea里全局搜索/clues

然后在这个controller里开始写对应的代码,类名不一定非要和老师的保持一致
但是要保证见名知意
在这个Controller里我们需要注意,一级窄化请求,Controller需要继承BaseController
与前人的代码风格保持一致
/**
* 线索跟进记录Controller
* @date 2021-04-22
*/
@RestController
@RequestMapping("/clues/record")
public class TbClueTrackRecordController extends BaseController {
}
然后开始写对应的方法
我们通过接口文档看到,传入参数
{
"clueId":9009, 线索id
"subject;":"1", 学科
"record":"根据记录", 根据记录
"level":"1", 意向级别
"type":"0", 0:正常跟进 1伪线索
"falseReason":"空号", 标记失败原因
"name":"张三", 姓名
"sex":"女", 性别
"weixin":"wx123456", 微信
"qq":"qq123456", QQ
"age":20 年龄
}
基于前端传入的json定义接收对象
public class ClueTrackRecordVo {
/** 线索id */
private Long clueId;
/** 学科 */
private String subject;
/** 跟进记录 */
private String record;
/** 意向等级 */
private String level;
/** 0 正常跟进记录 1 伪线索 */
private String type;
/** 原因 */
private String falseReason;
/** 客户姓名 */
private String name;
/** 1 男 0 女 */
private String sex;
/** 微信 */
private String weixin;
/** qq */
private String qq;
private Integer age;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private Date nextTime;
//自行生成get和set方法
}
⚠️注意:属性名和类型要一一对应
然后开始写我们的接口
前端传入的是json后端用什么接收@RequestBody
@Log(title = "线索跟进记录", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody ClueTrackRecordVo tbClueTrackRecord) {
xxx
}
对应的我们最终是需要插入到线索跟进记录表中
需要构建一个线索跟进对象的实体类,由于如果线索跟进的过程中线索数据被修改了,那么线索实体类的数据也要跟着修改,所以我们也还需要构建一个线索的实体类对象
调用insert操作插入线索跟进信息
// 添加一个日志
@Log(title = "线索跟进记录", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody ClueTrackRecordVo tbClueTrackRecord) {
//开始构建插入对象
TbClueTrackRecord trackRecord=new TbClueTrackRecord();
BeanUtils.copyProperties(tbClueTrackRecord,trackRecord);
trackRecord.setCreateTime(DateUtils.getNowDate());
trackRecord.setCreateBy(SecurityUtils.getUsername());
TbClue tbClue=new TbClue();
BeanUtils.copyProperties(tbClueTrackRecord,tbClue);
tbClue.setStatus(TbClue.StatusType.FOLLOWING.getValue()); //进行中
tbClue.setId(tbClueTrackRecord.getClueId());
//执行插入操作
return toAjax(tbClueTrackRecordService.insertTbClueTrackRecord(tbClue,trackRecord));
}
注意Insert操作需要去操作数据库,调用service
- 怎么调用service❓----@Autowired
- 调用哪个service❓---由于是跟进的业务创建对应的service
创建service
在哪创建service ----> 考虑到单一职责原则,在clue模块下创建
ITbClueTrackRecordService
public interface ITbClueTrackRecordService {
public int insertTbClueTrackRecord(TbClue tbClue, TbClueTrackRecord tbClueTrackRecord);
}
创建service的实现类
注意,由于需求我们了解,如果客户修改了线索的信息,那么线索对应的数据也要跟着修改
所以我们在添加根据记录的时候
- 先要修改线索的数据
- 再添加一条商机的跟进记录
ITbClueTrackRecordServiceImpl
@Override
@Transactional
public int insertTbClueTrackRecord(TbClue tbClue, TbClueTrackRecord tbClueTrackRecord) {
//先要修改线索的数据
tbClueMapper.updateTbClue(tbClue);
//再添加一条商机的跟进记录
return tbClueTrackRecordMapper.insertTbClueTrackRecord(tbClueTrackRecord);
}
接口名:TbClueTrackRecordMapper
/**
* 新增线索跟进记录
*
* @param tbClueTrackRecord 线索跟进记录
* @return 结果
*/
public int insertTbClueTrackRecord(TbClueTrackRecord tbClueTrackRecord);
文件名:TbClueTrackRecordMapper.xml
<insert id="insertTbClueTrackRecord" parameterType="TbClueTrackRecord" useGeneratedKeys="true" keyProperty="id">
insert into tb_clue_track_record
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="clueId != null and clueId != ''">clue_id,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
<if test="subject != null">subject,</if>
<if test="record != null">record,</if>
<if test="level != null">level,</if>
<if test="createTime != null">create_time,</if>
<if test="type != null">type,</if>
<if test="falseReason != null">false_reason,</if>
<if test="nextTime != null">next_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="clueId != null and clueId != ''">#{clueId},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
<if test="subject != null">#{subject},</if>
<if test="record != null">#{record},</if>
<if test="level != null">#{level},</if>
<if test="createTime != null">#{createTime},</if>
<if test="type != null">#{type},</if>
<if test="falseReason != null">#{falseReason},</if>
<if test="nextTime != null">#{nextTime},</if>
</trim>
</insert>
2.查询线索跟进记录列表 🚩
分析需求

通过阅读接口文档,了解到信息
- 方法名:/clues/record/list
- 请求方式GET
- 参数列表
- 传入参数:/clues/record/list?clueId=9009
- 线索id:clueId
- 传入参数:/clues/record/list?clueId=9009
- 返回值:
{
"total":1,
"rows":[
{
"createBy":"admin",
"createTime":"2021-11-20 16:30:05",
"updateBy":null,
"updateTime":null,
"id":237,
"clueId":9009,
"subject":"6",
"record":"下次继续跟进",
"level":"0",
"type":"0",
"falseReason":null,
"nextTime":"2021-11-25 12:00"
}
],
"code":200,
"msg":"查询成功",
"params":null
}
步骤
- 根据请求的路径确定编写代码的位置/clues下,对应的就是找/clues这个一级窄化请求对应的controller
- 我们在idea里全局搜索/clues
- 没有找到,没有找到怎么办?--自己建一个Controller
- 问题:在哪创建controller❓,所有的controller都要写在admin工程里
- 由于是线索跟进,所以在clues下创建一个controller
- 问题:在哪创建controller❓,所有的controller都要写在admin工程里
- 没有找到,没有找到怎么办?--自己建一个Controller
- 我们在idea里全局搜索/clues

然后在这个controller里开始写对应的代码,类名不一定非要和老师的保持一致 友情提醒
但是要保证见名知意,在这个Controller里我们需要注意,一级窄化请求,Controller需要继承BaseController
与前人的代码风格保持一致 👍
/**
* 线索跟进记录Controller
* @date 2021-04-22
*/
@RestController
@RequestMapping("/clues")
public class TbClueTrackRecordController extends BaseController {
}
然后开始写对应的方法,我们通过接口文档看到,传入参数?clueId=9009
返回值类型我们看接口文档
{
"total":1,
"rows":[
{
"createBy":"admin",
"createTime":"2021-11-20 16:30:05",
"updateBy":null,
"updateTime":null,
"id":237,
"clueId":9009,
"subject":"6",
"record":"下次继续跟进",
"level":"0",
"type":"0",
"falseReason":null,
"nextTime":"2021-11-25 12:00"
}
],
"code":200,
"msg":"查询成功",
"params":null
}
发现参数有total,rows,code,msg,params,证明我们需要返回的是一个分页数据,针对分页数据rows中的属性
构建返回对象
package com.huike.clues.domain;
import java.util.Date;
/**
* 线索跟进记录对象 tb_clue_track_record
*/
public class TbClueTrackRecord extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 任务id */
private Long id;
/** 线索id */
private Long clueId;
/** 意向等级 */
private String subject;
/** 跟进记录 */
private String record;
/** 意向等级 */
private String level;
/** 0 正常跟进记录 1 伪线索 */
private String type;
/** 原因 */
private String falseReason;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private Date nextTime;
//自行补充getset方法
}
然后开始写我们的接口,前端传值后端 用什么接收?--->@RequestParam("clueId")
@PreAuthorize("@ss.hasPermi('clues:record:list')")
@GetMapping("/list")
public TableDataInfo list(@RequestParam("clueId")Long clueId) {
xxx
}
参考前人写的分页代码
/**
* 查询线索跟进记录列表
*/
@PreAuthorize("@ss.hasPermi('clues:record:list')")
@GetMapping("/list")
public TableDataInfo list(@RequestParam("clueId")Long clueId) {
startPage();
//在这里执行查询
List<TbClueTrackRecord> list = 找serivce要数据
return getDataTable(list);
}
构建一个service层的方法
public List<TbClueTrackRecord> selectTbClueTrackRecordList(Long clueId);
/**
* 根据线索id查询线索跟进记录
*/
@Override
public List<TbClueTrackRecord> selectTbClueTrackRecordList(Long clueId) {
return tbClueTrackRecordMapper.selectTbClueTrackRecordListByClueId(clueId);
}
TbClueTrackRecordMapper
/**
* 根据线索id查询线索跟进记录
* @param clueId
* @return
*/
public List<TbClueTrackRecord> selectTbClueTrackRecordListByClueId(Long clueId);
sql语句
SELECT id, clue_id, create_by, subject, record, level, create_time, type, false_reason, next_time
FROM tb_clue_track_record
WHERE clue_id = 9007
TbClueTrackRecordMapper.xml
<select id="selectTbClueTrackRecordListByClueId" parameterType="Long" resultMap="TbClueTrackRecordResult">
<include refid="selectTbClueTrackRecordVo"/>
<where>
<if test="clueId != null and clueId != ''"> and clue_id = #{clueId}</if>
</where>
</select>
3.商机跟进记录列表 🚩
分析需求
对应的页面:

- 方法名:
/business/record/list
- 请求方式:
GET
- 参数列表:
- 传入参数:
businessId:商机id
- 传入参数:
- 返回值:
{
"msg":"操作成功",
"code":200,
"data":[
{
"createBy":"admin",
"createTime":"2021-11-19 17:21:14",
"updateBy":null,
"updateTime":null,
"id":8437,
"businessId":3392,
"keyItems":"5,3", //沟通重点Id
"keys":[ //沟通重点
"师资",
"位置"
],
"record":"1",
"trackStatus":"1", //跟进状态
"nextTime":"2021-11-27 12:00"
}
]
}
步骤
2.2.1 设计传入参数
商机id:businessId
2.2.2 编写控制层
/**
* 查询商机跟进记录列表
*/
@PreAuthorize("@ss.hasPermi('business:record:list')")
@GetMapping("/list")
public AjaxResult list(@RequestParam("businessId")Long id){
// 查询商机跟进列表
List<TbBusinessTrackRecord> list= tbBusinessTrackRecordService.selectTbBusinessTrackRecordList(id);
// 设置关键词
for (TbBusinessTrackRecord businessTrackRecord : list) {
// 将字符串进行切割
String[] items= businessTrackRecord.getKeyItems().split(",");
for (String item : items) {
// communication_point 代表字段表中的字段类型
String dictLable= sysDictDataService.selectDictLabel("communication_point",item);
// 添加查询后的字典值
businessTrackRecord.getKeys().add(dictLable);
}
}
return AjaxResult.success(list);
}
2.2.3 编写Service层
ITbBusinessTrackRecordService
/**
* 跟进商机id查询商机跟进记录
* @param id
* @return
*/
public List<TbBusinessTrackRecord> selectTbBusinessTrackRecordList(Long id);
TbBusinessTrackRecordServiceImpl
/**
* 跟进商机id查询商机跟进记录
*/
@Override
public List<TbBusinessTrackRecord> selectTbBusinessTrackRecordList(Long id) {
return tbBusinessTrackRecordMapper.selectTbBusinessTrackRecordListByBusinessId(id);
}
ISysDictDataService
/**
* 根据字典类型和字典键值查询字典数据信息
*
* @param dictType 字典类型
* @param dictValue 字典键值
* @return 字典标签
*/
public String selectDictLabel(String dictType, String dictValue);
ISysDictDataServiceImpl
/**
* 根据字典类型和字典键值查询字典数据信息
*
* @param dictType 字典类型
* @param dictValue 字典键值
* @return 字典标签
*/
@Override
public String selectDictLabel(String dictType, String dictValue){
return dictDataMapper.selectDictLabel(dictType, dictValue);
}
2.2.3 编写Mapper层
查看商机跟进记录 TbBusinessTrackRecordMapper
/**
* 跟进商机id查询商机跟进记录
* @param id
* @return
*/
public List<TbBusinessTrackRecord> selectTbBusinessTrackRecordListByBusinessId(Long id);
TbBusinessTrackRecordMapper.xml
<select id="selectTbBusinessTrackRecordListByBusinessId" parameterType="Long" resultMap="TbBusinessTrackRecordResult">
<include refid="selectTbBusinessTrackRecordVo"/>
<where>
<if test="businessId != null and businessId != ''"> and business_id = #{businessId}</if>
</where>
</select>
通过字典类型和字典键值查询数据
SysDictDataMapper
/**
* 根据字典类型和字典键值查询字典数据信息
*
* @param dictType 字典类型
* @param dictValue 字典键值
* @return 字典标签
*/
public String selectDictLabel(@Param("dictType") String dictType, @Param("dictValue") String dictValue);
SysDictDataMapper.xml
<select id="selectDictLabel" resultType="String">
select dict_label from sys_dict_data
where dict_type = #{dictType} and dict_value = #{dictValue}
</select>
4. 新增商机跟进记录 🚩
分析需求
对应的页面:

- 方法名:
/business/record
- 请求方式:
POST
- 参数列表:
- 传入参数:
{
"id":"",
"name":"韩人",
"phone":"13811111400",
"occupation":"4",
"education":"5",
"provinces":"北京市",
"city":"市辖区",
"weixin":"w123245743",
"age":20, 年龄
"major":"8", 专业
"job":"2", 专业
"salary":"3", 薪资
"qq":"99880276", QQ
"sex":"0", 性别id
"expectedSalary":"4", 目标薪资 对应字典id
"remark":"备注信息", 备注信息
"subject":"1", 学科id
"reasons":"学习原因", 学习原因
"plan":"职业计划", 职业计划
"planTime":"2022-03-14", 计划学习
"courseId":12, 课程id
"otherIntention":"其他意向", 其他意向
"trackStatus":"1", 跟进状态
"nextTime":"2022-03-15 12:00", 下次跟进时间
"keyItems":"3,1,2", 沟通重点
"record":"沟通纪要", 沟通纪要
"createBy":"blackman", 创建人
"createTime":"2021-11-16 15:34:12", 创建时间
"channel":"0", 渠道id
"activityId":73, 活动id
"businessId":"3392" 商机id
}
- 返回值:
{"msg":"操作成功","code":200}
步骤
2.1.1 设计VO对象
BusinessTrackVo
package com.huike.business.domain.vo;
public class BusinessTrackVo {
private Long businessId;
/** 客户姓名 */
private String name;
/** 手机号 */
private String phone;
/** 渠道 */
private Long channelId;
/** 活动id */
private Long activityId;
/** 省 */
@Excel(name = "省")
private String provinces;
/** 区 */
@Excel(name = "区")
private String city;
/** 男或者女 */
private String sex;
/** 年龄 */
private Integer age;
/** 微信 */
private String weixin;
/** qq */
private String qq;
/** 意向等级 */
private String level;
/** 意向学科 */
private String subject;
/** 课程 */
private Long courseId;
/** 职业 */
private String occupation;
/** 学历 */
private String education;
/** 在职情况 */
private String job;
/** 薪资 */
private String salary;
/** 专业 */
private String major;
/** 希望薪资 */
private String expectedSalary;
/** 学习原因 */
private String reasons;
/** 职业计划 */
private String plan;
/** 计划时间 */
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "计划时间", width = 30, dateFormat = "yyyy-MM-dd")
private Date planTime;
/** 其他意向 */
@Excel(name = "其他意向")
private String otherIntention;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private Date nextTime;
//沟通备注
private String remark;
//沟通重点
private String keyItems;
/** 沟通纪要 */
private String record;
/** 跟进状态 */
private String trackStatus;
//todo 自行生成getset方法
}
2.1.2 编写控制层
/**
* 新增商机跟进记录
*/
@PreAuthorize("@ss.hasPermi('business:record:add')")
@Log(title = "商机跟进记录", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BusinessTrackVo businessTrackVo){
System.out.println("-------"+businessTrackVo);
TbBusinessTrackRecord trackRecord=new TbBusinessTrackRecord();
BeanUtils.copyProperties(businessTrackVo,trackRecord);
trackRecord.setCreateTime(DateUtils.getNowDate());
trackRecord.setCreateBy(SecurityUtils.getUsername());
TbBusiness business=new TbBusiness();
BeanUtils.copyProperties(businessTrackVo,business);
business.setStatus(TbBusiness.StatusType.FOLLOWING.getValue());
business.setId(businessTrackVo.getBusinessId());
return toAjax(tbBusinessTrackRecordService.insertTbBusinessTrackRecord(business,trackRecord));
}
2.1.3 编写Service层
ITbBusinessTrackRecordService
/**
* 新增商机跟进记录
* @param tbBusiness
* @param tbBusinessTrackRecord
* @return
*/
public int insertTbBusinessTrackRecord(TbBusiness tbBusiness,TbBusinessTrackRecord t);
TbBusinessTrackRecordServiceImpl
@Override
public int insertTbBusinessTrackRecord(TbBusiness tbBusiness, TbBusinessTrackRecord t) {
//更新商机
tbBusinessMapper.updateTbBusiness(tbBusiness);
// 插入商机跟进记录
return tbBusinessTrackRecordMapper.insertTbBusinessTrackRecord(t);
}
2.1.3 编写Mapper层
修改商机转态
TbBusinessMapper
/**
* 修改商机
*
* @param tbBusiness 商机
* @return 结果
*/
public int updateTbBusiness(TbBusiness tbBusiness);
TbBusinessMapper.xml
suffix 和 suffixOverrides 使用场景介绍 trim 标签的作用是:在 status = ? 后面裁剪了逗号,而且自动加了一个 set 前缀和 where 后缀。 suffix 是给整个字符串增加一个后缀,而 suffixOverrides 则是去掉整个字符串后面多余的字符。
<update id="updateTbBusiness" parameterType="TbBusiness">
update tb_business
<trim prefix="SET" suffixOverrides=",">
<if test="name != null">name = #{name},</if>
<if test="phone != null">phone = #{phone},</if>
<if test="channel != null">channel = #{channel},</if>
<if test="activityId != null">activity_id = #{activityId},</if>
<if test="provinces != null">provinces = #{provinces},</if>
<if test="city != null">city = #{city},</if>
<if test="sex != null and sex != ''">sex = #{sex},</if>
<if test="age != null">age = #{age},</if>
<if test="weixin != null">weixin = #{weixin},</if>
<if test="qq != null">qq = #{qq},</if>
<if test="level != null">level = #{level},</if>
<if test="subject != null">subject = #{subject},</if>
<if test="courseId != null">course_id = #{courseId},</if>
<if test="createBy != null">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="occupation != null">occupation = #{occupation},</if>
<if test="education != null">education = #{education},</if>
<if test="job != null">job = #{job},</if>
<if test="salary != null">salary = #{salary},</if>
<if test="major != null">major = #{major},</if>
<if test="expectedSalary != null">expected_salary = #{expectedSalary},</if>
<if test="reasons != null">reasons = #{reasons},</if>
<if test="plan != null">plan = #{plan},</if>
<if test="planTime != null">plan_time = #{planTime},</if>
<if test="otherIntention != null">other_intention = #{otherIntention},</if>
<if test="nextTime != null">next_time = #{nextTime},</if>
<if test="status != null">status = #{status},</if>
</trim>
where id = #{id}
</update>
添加商机跟进状态
TbBusinessTrackRecordMapper
/**
* 新增商机跟进记录
*
* @param tbBusinessTrackRecord 商机跟进记录
* @return 结果
*/
public int insertTbBusinessTrackRecord(TbBusinessTrackRecord tbBusinessTrackRecord);
TbBusinessTrackRecordMapper.xml
<insert id="insertTbBusinessTrackRecord" parameterType="TbBusinessTrackRecord" useGeneratedKeys="true" keyProperty="id">
insert into tb_business_track_record
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="businessId != null and businessId != ''">business_id,</if>
<if test="createBy != null and createBy != ''">create_by,</if>
<if test="keyItems != null">key_items,</if>
<if test="record != null">record,</if>
<if test="createTime != null">create_time,</if>
<if test="trackStatus != null">track_status,</if>
<if test="nextTime != null">next_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="businessId != null and businessId != ''">#{businessId},</if>
<if test="createBy != null and createBy != ''">#{createBy},</if>
<if test="keyItems != null">#{keyItems},</if>
<if test="record != null">#{record},</if>
<if test="createTime != null">#{createTime},</if>
<if test="trackStatus != null">#{trackStatus},</if>
<if test="nextTime != null">#{nextTime},</if>
</trim>
</insert>