diff --git a/coolstore-partner-common/src/main/java/com/cool/store/enums/ErrorCodeEnum.java b/coolstore-partner-common/src/main/java/com/cool/store/enums/ErrorCodeEnum.java index c1c4b0772..d4c44ec88 100644 --- a/coolstore-partner-common/src/main/java/com/cool/store/enums/ErrorCodeEnum.java +++ b/coolstore-partner-common/src/main/java/com/cool/store/enums/ErrorCodeEnum.java @@ -373,6 +373,11 @@ public enum ErrorCodeEnum { TP_EXIST_PENDING_IMPORT_TASK(1810012, "存在导入中的任务,请稍后再试", null), TP_NOT_EXIST_FORM_TYPE(1810013, "不存在该表单类型", null), + VISIT_RECORD_NOT_EXIST(1820001, "拜访记录不存在", null), + VISIT_RECORD_NOT_COMPLETE_EDIT(1820002, "未编辑拜访信息,无法签退", null), + VISIT_RECORD_INVALID(1820003, "拜访记录已失效", null), + VISIT_RECORD_NOT_YOUR_OWN(1820004, "非拜访人,无法操作", null), + VISIT_RECORD_COMPLETE(1820005, "已完成拜访", null), CITY_PLANNING_EXISTS(1820001, "同年份同季度同省市已存在规划,不能重复添加", null), ; diff --git a/coolstore-partner-common/src/main/java/com/cool/store/enums/RocketMqGroupEnum.java b/coolstore-partner-common/src/main/java/com/cool/store/enums/RocketMqGroupEnum.java index 73b600d3f..576928f44 100644 --- a/coolstore-partner-common/src/main/java/com/cool/store/enums/RocketMqGroupEnum.java +++ b/coolstore-partner-common/src/main/java/com/cool/store/enums/RocketMqGroupEnum.java @@ -37,7 +37,7 @@ public enum RocketMqGroupEnum { SHOP_DECORATION_ASSIGN("shop_decoration_assign", new ArrayList<>(Arrays.asList(RocketMqTagEnum.DELAY_SHOP_DECORATION_ASSIGN))), STORE_MASTER_ISSUE("store_master_issue", new ArrayList<>(Arrays.asList(RocketMqTagEnum.STORE_MASTER_ISSUE))), - + VISIT_RECORD_INVALID("visit_record_invalid", new ArrayList<>(Arrays.asList(RocketMqTagEnum.VISIT_RECORD_INVALID))), ; private final String group; diff --git a/coolstore-partner-common/src/main/java/com/cool/store/enums/RocketMqTagEnum.java b/coolstore-partner-common/src/main/java/com/cool/store/enums/RocketMqTagEnum.java index f630aebb8..90a73f27e 100644 --- a/coolstore-partner-common/src/main/java/com/cool/store/enums/RocketMqTagEnum.java +++ b/coolstore-partner-common/src/main/java/com/cool/store/enums/RocketMqTagEnum.java @@ -21,6 +21,7 @@ public enum RocketMqTagEnum { STORE_USER_UPDATE("store_user_update", "门店信息人员变更同步菜品"), DELAY_SHOP_DECORATION_ASSIGN("shop_decoration_assign","门店装修分配"), STORE_MASTER_ISSUE("store_master_issue","门店主数据下发"), + VISIT_RECORD_INVALID("visit_record_invalid", "拜访记录失效"), ; diff --git a/coolstore-partner-common/src/main/java/com/cool/store/enums/visit/VisitStatusEnum.java b/coolstore-partner-common/src/main/java/com/cool/store/enums/visit/VisitStatusEnum.java new file mode 100644 index 000000000..75731139b --- /dev/null +++ b/coolstore-partner-common/src/main/java/com/cool/store/enums/visit/VisitStatusEnum.java @@ -0,0 +1,39 @@ +package com.cool.store.enums.visit; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + *

+ * 拜访记录状态枚举类 + *

+ * + * @author wangff + * @since 2025/12/8 + */ +@Getter +@AllArgsConstructor +public enum VisitStatusEnum { + + N_SIGN_IN(0, "待签到"), + + N_SIGN_OUT(1, "待签退"), + + COMPLETE(2, "已完成"), + + INVALID(3, "已失效"), + ; + + private final Integer status; + + private final String desc; + + public static String getDescByStatus(Integer status) { + for (VisitStatusEnum value : values()) { + if (value.status.equals(status)) { + return value.desc; + } + } + return null; + } +} diff --git a/coolstore-partner-dao/src/main/java/com/cool/store/dao/store/StoreMasterSignerInfoDAO.java b/coolstore-partner-dao/src/main/java/com/cool/store/dao/store/StoreMasterSignerInfoDAO.java index b23f6492b..fb687e777 100644 --- a/coolstore-partner-dao/src/main/java/com/cool/store/dao/store/StoreMasterSignerInfoDAO.java +++ b/coolstore-partner-dao/src/main/java/com/cool/store/dao/store/StoreMasterSignerInfoDAO.java @@ -6,7 +6,9 @@ import com.cool.store.mapper.store.StoreMasterSignerInfoMapper; import lombok.RequiredArgsConstructor; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.entity.Example; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -41,4 +43,15 @@ public class StoreMasterSignerInfoDAO { public StoreMasterSignerInfoDO getByStoreId(String storeId) { return storeMasterSignerInfoMapper.selectOne(StoreMasterSignerInfoDO.builder().storeId(storeId).build()); } + + public List getListByIdCard(Collection idCards) { + if (CollectionUtils.isEmpty(idCards)) { + return Collections.emptyList(); + } + Example example = new Example(StoreMasterSignerInfoDO.class); + example.createCriteria() + .orIn("signer1IdCardNo", idCards) + .orIn("signer2IdCardNo", idCards); + return storeMasterSignerInfoMapper.selectByExample(example); + } } diff --git a/coolstore-partner-dao/src/main/java/com/cool/store/dao/visit/VisitRecordDAO.java b/coolstore-partner-dao/src/main/java/com/cool/store/dao/visit/VisitRecordDAO.java new file mode 100644 index 000000000..f8f0ff9f7 --- /dev/null +++ b/coolstore-partner-dao/src/main/java/com/cool/store/dao/visit/VisitRecordDAO.java @@ -0,0 +1,126 @@ +package com.cool.store.dao.visit; + +import com.alibaba.excel.util.CollectionUtils; +import com.cool.store.entity.visit.VisitRecordDO; +import com.cool.store.enums.visit.VisitStatusEnum; +import com.cool.store.mapper.visit.VisitRecordMapper; +import com.cool.store.request.visit.VisitRecordQueryRequest; +import com.cool.store.vo.visit.VisitPartnerListVO; +import com.cool.store.vo.visit.VisitRecordListVO; +import com.cool.store.vo.visit.VisitRecordStatusStatisticsVO; +import lombok.AllArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; +import tk.mybatis.mapper.entity.Example; + +import java.util.Collections; +import java.util.Date; +import java.util.List; + +/** + *

+ * 拜访记录DAO + *

+ * + * @author wangff + * @since 2025/12/8 + */ +@Repository +@AllArgsConstructor +public class VisitRecordDAO { + private final VisitRecordMapper visitRecordMapper; + + public Boolean insertSelective(VisitRecordDO visitRecordDO) { + return visitRecordMapper.insertSelective(visitRecordDO) > 0; + } + + public Boolean updateSelective(VisitRecordDO visitRecordDO) { + return visitRecordMapper.updateByPrimaryKeySelective(visitRecordDO) > 0; + } + + public VisitRecordDO getById(Long id) { + return visitRecordMapper.selectByPrimaryKey(id); + } + + /** + * 查询拜访加盟商 + * @param regionIds 加盟商所属大区id + * @param keyword 加盟商姓名或手机号 + * @return 拜访加盟商列表VO + */ + public List getPartnerList(List regionIds, String keyword, String userId) { + return visitRecordMapper.getPartnerList(regionIds, keyword, userId); + } + + /** + * 查询拜访加盟商线索id + * @param regionIds 加盟商所属大区id + * @param keyword 加盟商姓名或手机号 + * @param userId 拜访用户id + * @return 线索id列表 + */ + public List getPartnerLineId(List regionIds, String keyword, String userId) { + return visitRecordMapper.getPartnerLineId(regionIds, keyword, userId); + } + + /** + * 根据线索id查询拜访记录列表 + * @param lineIds 线索id + * @return 拜访记录列表VO列表 + */ + public List getVisitRecordListByLineIds(List lineIds, String userId) { + if (CollectionUtils.isEmpty(lineIds)) { + return Collections.emptyList(); + } + return visitRecordMapper.getVisitRecordListByLineIds(lineIds, userId); + } + + /** + * 根据线索查询 + * @param lineId 线索id + * @param startDate 开始日期 + * @param endDate 结束日期 + * @return 拜访记录列表 + */ + public List getByLineId(Long lineId, Date startDate, Date endDate, String userId) { + Example example = new Example(VisitRecordDO.class); + Example.Criteria criteria = example.createCriteria().andEqualTo("lineId", lineId) + .andGreaterThanOrEqualTo("visitDate", startDate) + .andLessThanOrEqualTo("visitDate", endDate); + if (StringUtils.isNotBlank(userId)) { + criteria.andEqualTo("userId", userId); + } + example.setOrderByClause("create_time DESC"); + return visitRecordMapper.selectByExample(example); + } + + public Boolean deleteById(Long id) { + return visitRecordMapper.deleteByPrimaryKey(id) > 0; + } + + /** + * 拜访记录列表 + * @param request 拜访记录查询Request + * @return 拜访记录列表VO列表 + */ + public List getVisitRecordList(VisitRecordQueryRequest request) { + return visitRecordMapper.getVisitRecordList(request); + } + + /** + * 根据线索id查询已完成拜访数量 + * @param lineId 线索id + * @return 数量 + */ + public Integer getCompleteVisitNum(Long lineId) { + return visitRecordMapper.selectCount(VisitRecordDO.builder().lineId(lineId).status(VisitStatusEnum.COMPLETE.getStatus()).build()); + } + + /** + * 统计 + */ + public VisitRecordStatusStatisticsVO countStatusStatistics(VisitRecordQueryRequest request) { + return visitRecordMapper.countStatusStatistics(request); + } +} diff --git a/coolstore-partner-dao/src/main/java/com/cool/store/mapper/RegionMapper.java b/coolstore-partner-dao/src/main/java/com/cool/store/mapper/RegionMapper.java index 29cb18e1d..df82b169b 100644 --- a/coolstore-partner-dao/src/main/java/com/cool/store/mapper/RegionMapper.java +++ b/coolstore-partner-dao/src/main/java/com/cool/store/mapper/RegionMapper.java @@ -87,6 +87,8 @@ public interface RegionMapper { List getSubRegionIdsByRegionIds( @Param("regionIds")List regionIds); + List getSubRegionIdsExcludeStore(@Param("regionIds")List regionIds); + List getSubRegionIdsByRegionIdList( @Param("regionIds")List regionIds); List getStoreIdsByRegionIds( @Param("regionIds")List regionIds); diff --git a/coolstore-partner-dao/src/main/java/com/cool/store/mapper/oplog/SysLogMapper.java b/coolstore-partner-dao/src/main/java/com/cool/store/mapper/oplog/SysLogMapper.java new file mode 100644 index 000000000..264b647a9 --- /dev/null +++ b/coolstore-partner-dao/src/main/java/com/cool/store/mapper/oplog/SysLogMapper.java @@ -0,0 +1,7 @@ +package com.cool.store.mapper.oplog; + +import com.cool.store.entity.oplog.SysLogDO; +import tk.mybatis.mapper.common.Mapper; + +public interface SysLogMapper extends Mapper { +} \ No newline at end of file diff --git a/coolstore-partner-dao/src/main/java/com/cool/store/mapper/visit/VisitRecordMapper.java b/coolstore-partner-dao/src/main/java/com/cool/store/mapper/visit/VisitRecordMapper.java new file mode 100644 index 000000000..f63426cfb --- /dev/null +++ b/coolstore-partner-dao/src/main/java/com/cool/store/mapper/visit/VisitRecordMapper.java @@ -0,0 +1,57 @@ +package com.cool.store.mapper.visit; + +import com.cool.store.entity.visit.VisitRecordDO; +import com.cool.store.request.visit.VisitRecordQueryRequest; +import com.cool.store.vo.visit.VisitPartnerListVO; +import com.cool.store.vo.visit.VisitRecordListVO; +import com.cool.store.vo.visit.VisitRecordStatusStatisticsVO; +import org.apache.ibatis.annotations.Param; +import tk.mybatis.mapper.common.Mapper; + +import java.util.List; + +public interface VisitRecordMapper extends Mapper { + + /** + * 查询拜访加盟商 + * @param regionIds 加盟商所属大区id + * @param keyword 加盟商姓名或手机号 + * @return 拜访加盟商列表VO + */ + List getPartnerList(@Param("regionIds") List regionIds, + @Param("keyword") String keyword, + @Param("userId") String userId); + + /** + * 拜访记录列表 + * @param request 拜访记录查询Request + * @return 拜访记录列表VO列表 + */ + List getVisitRecordList(@Param("request") VisitRecordQueryRequest request); + + /** + * 查询拜访加盟商线索id + * @param regionIds 加盟商所属大区id + * @param keyword 加盟商姓名或手机号 + * @param userId 拜访用户id + * @return 线索id列表 + */ + List getPartnerLineId(@Param("regionIds") List regionIds, + @Param("keyword") String keyword, + @Param("userId") String userId); + + /** + * 根据线索id查询拜访记录列表 + * @param lineIds 线索id + * @return 拜访记录列表VO列表 + */ + List getVisitRecordListByLineIds(@Param("lineIds") List lineIds, @Param("userId") String userId); + + /** + * 统计:本月已完成数量、全部已完成数量、全部未完成数量、全部已失效数量 + * - 本月口径:按 visit_date 在本月范围内且 status=2 + * - 全部口径:不限制月份 + */ + VisitRecordStatusStatisticsVO countStatusStatistics(@Param("request") VisitRecordQueryRequest request); + +} \ No newline at end of file diff --git a/coolstore-partner-dao/src/main/resources/mapper/RegionMapper.xml b/coolstore-partner-dao/src/main/resources/mapper/RegionMapper.xml index f7aea2ac8..92c0973c7 100644 --- a/coolstore-partner-dao/src/main/resources/mapper/RegionMapper.xml +++ b/coolstore-partner-dao/src/main/resources/mapper/RegionMapper.xml @@ -285,6 +285,17 @@ region_path like concat("%/", #{region}, "/%") + + + SELECT a.line_id, a.visit_num, b.username name, b.mobile + + , a.my_visit_num + + FROM ( + SELECT line_id, COUNT(1) visit_num + + , SUM(CASE WHEN user_id = #{userId} THEN 1 ELSE 0 END) my_visit_num + + FROM zxjp_visit_record + WHERE status = 2 + GROUP BY line_id + ) a + INNER JOIN xfsg_line_info b ON a.line_id = b.id AND b.deleted = 0 + + + AND a.my_visit_num > 0 + + + AND b.region_id IN + + #{regionId} + + + + AND (b.mobile LIKE CONCAT('%', #{keyword}, '%') OR b.username LIKE CONCAT('%', #{keyword}, '%')) + + + + + + + + + + + + \ No newline at end of file diff --git a/coolstore-partner-model/src/main/java/com/cool/store/entity/oplog/SysLogDO.java b/coolstore-partner-model/src/main/java/com/cool/store/entity/oplog/SysLogDO.java new file mode 100644 index 000000000..ada87e138 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/entity/oplog/SysLogDO.java @@ -0,0 +1,122 @@ +package com.cool.store.entity.oplog; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; +import javax.persistence.*; + +@Table(name = "sys_log_${enterpriseId}") +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class SysLogDO { + /** + * 主键 + */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + /** + * 菜单 + */ + private String menus; + + /** + * 模块,取最后一级菜单 + */ + private String module; + + /** + * 功能 + */ + private String func; + + /** + * 子功能 + */ + @Column(name = "sub_func") + private String subFunc; + + /** + * 操作人id + */ + @Column(name = "op_user_id") + private String opUserId; + + /** + * 操作人名称 + */ + @Column(name = "op_user_name") + private String opUserName; + + /** + * 操作人手机号 + */ + @Column(name = "op_user_mobile") + private String opUserMobile; + + /** + * 操作时间 + */ + @Column(name = "op_time") + private Date opTime; + + /** + * 操作类型 + */ + @Column(name = "op_type") + private String opType; + + /** + * 操作内容 + */ + @Column(name = "op_content") + private String opContent; + + /** + * 登录ip + */ + @Column(name = "op_ip") + private String opIp; + + /** + * 设备信息 + */ + @Column(name = "device_info") + private String deviceInfo; + + /** + * 请求参数 + */ + @Column(name = "req_params") + private String reqParams; + + /** + * 响应参数 + */ + @Column(name = "resp_params") + private String respParams; + + /** + * 请求路径 + */ + private String url; + + /** + * 扩展字段 + */ + @Column(name = "extend_info") + private String extendInfo; + + /** + * 操作人工号 + */ + @Column(name = "op_user_jobnumber") + private String opUserJobnumber; + +} \ No newline at end of file diff --git a/coolstore-partner-model/src/main/java/com/cool/store/entity/visit/VisitRecordDO.java b/coolstore-partner-model/src/main/java/com/cool/store/entity/visit/VisitRecordDO.java new file mode 100644 index 000000000..f54262c18 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/entity/visit/VisitRecordDO.java @@ -0,0 +1,142 @@ +package com.cool.store.entity.visit; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; +import javax.persistence.*; + +/** + * 拜访记录 + */ +@Data +@Table(name = "zxjp_visit_record") +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class VisitRecordDO { + /** + * id + */ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + /** + * 拜访编号 + */ + @Column(name = "visit_no") + private String visitNo; + + /** + * 线索id + */ + @Column(name = "line_id") + private Long lineId; + + /** + * 拜访状态,0待签到 1待签退 2已完成 3已失效 + */ + private Integer status; + + /** + * 是否已完成拜访信息编辑 0否 1是 + */ + @Column(name = "complete_edit") + private Integer completeEdit; + + /** + * 签到时间 + */ + @Column(name = "sign_in_time") + private Date signInTime; + + /** + * 签到经纬度 + */ + @Column(name = "sign_in_lon_lat") + private String signInLonLat; + + /** + * 签到地址 + */ + @Column(name = "sign_in_address") + private String signInAddress; + + /** + * 签退时间 + */ + @Column(name = "sign_out_time") + private Date signOutTime; + + /** + * 签退经纬度 + */ + @Column(name = "sign_out_lon_lat") + private String signOutLonLat; + + /** + * 签退地址 + */ + @Column(name = "sign_out_address") + private String signOutAddress; + + /** + * 拜访日期 + */ + @Column(name = "visit_date") + private Date visitDate; + + /** + * 开新店意愿 + */ + private String desire; + + /** + * 意向开店城市 + */ + @Column(name = "desire_city") + private String desireCity; + + /** + * 具体区域 + */ + @Column(name = "desire_district") + private String desireDistrict; + + /** + * 是否对应现有铺位 + */ + @Column(name = "existing_shop_point") + private Integer existingShopPoint; + + /** + * 拜访备注 + */ + private String remark; + + /** + * 创建时间 + */ + @Column(name = "create_time") + private Date createTime; + + /** + * 更新时间 + */ + @Column(name = "update_time") + private Date updateTime; + + /** + * 加盟商合影图片列表 + */ + private String photos; + + /** + * 拜访用户id + */ + @Column(name = "user_id") + private String userId; +} \ No newline at end of file diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitCancelRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitCancelRequest.java new file mode 100644 index 000000000..58cb96b2c --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitCancelRequest.java @@ -0,0 +1,18 @@ +package com.cool.store.request.visit; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + *

+ * 拜访记录取消Request + *

+ * + * @author wangff + * @since 2025/12/9 + */ +@Data +public class VisitCancelRequest { + @ApiModelProperty("拜访记录id") + private Long id; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitEditRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitEditRequest.java new file mode 100644 index 000000000..2d18b8ec7 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitEditRequest.java @@ -0,0 +1,47 @@ +package com.cool.store.request.visit; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.Date; + +/** + *

+ * 拜访记录编辑Request + *

+ * + * @author wangff + * @since 2025/12/8 + */ +@Data +public class VisitEditRequest { + @ApiModelProperty("拜访记录id") + @NotNull(message = "拜访记录id不能为空") + private Long id; + + @ApiModelProperty("拜访日期") + @NotNull(message = "拜访日期不能为空") + private Date visitDate; + + @ApiModelProperty("开新店意愿,字典表visit_desire") + @NotBlank(message = "开新店意愿不能为空") + private String desire; + + @ApiModelProperty("意向开店城市") + private String desireCity; + + @ApiModelProperty("具体区域") + private String desireDistrict; + + @ApiModelProperty("是否对应现有铺位") + private Integer existingShopPoint; + + @ApiModelProperty("拜访备注") + private String remark; + + @ApiModelProperty("加盟商合影图片列表") + private String photos; + +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitPartnerQueryRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitPartnerQueryRequest.java new file mode 100644 index 000000000..a228070ba --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitPartnerQueryRequest.java @@ -0,0 +1,22 @@ +package com.cool.store.request.visit; + +import com.cool.store.common.PageBasicInfo; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + *

+ * 拜访加盟商查询Request + *

+ * + * @author wangff + * @since 2025/12/9 + */ +@Data +public class VisitPartnerQueryRequest extends PageBasicInfo { + @ApiModelProperty("加盟商姓名或手机号") + private String keyword; + + @ApiModelProperty(value = "拜访用户id,查询我的拜访时传当前用户", hidden = true) + private String userId; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitRecordQueryRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitRecordQueryRequest.java new file mode 100644 index 000000000..019d37b50 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitRecordQueryRequest.java @@ -0,0 +1,42 @@ +package com.cool.store.request.visit; + +import com.cool.store.common.PageBasicInfo; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + *

+ * 拜访记录查询Request + *

+ * + * @author wangff + * @since 2025/12/9 + */ +@Data +public class VisitRecordQueryRequest extends PageBasicInfo { + @ApiModelProperty("线索id") + private Long lineId; + + @ApiModelProperty("加盟商姓名或手机号") + private String keyword; + + @ApiModelProperty("拜访编号") + private String visitNo; + + @ApiModelProperty("拜访人") + private String visitUserId; + + @ApiModelProperty("拜访开始日期") + private Date visitStartDate; + + @ApiModelProperty("拜访结束日期") + private Date visitEndDate; + + @ApiModelProperty("拜访状态,0待签到 1待签退 2已完成 3已失效") + private Integer status; + + @ApiModelProperty(value = "拜访人id", hidden = true) + private String userId; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitRecordSimpleQueryRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitRecordSimpleQueryRequest.java new file mode 100644 index 000000000..6faff2571 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitRecordSimpleQueryRequest.java @@ -0,0 +1,32 @@ +package com.cool.store.request.visit; + +import com.cool.store.common.PageBasicInfo; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.Date; + +/** + *

+ * 拜访记录查询Request + *

+ * + * @author wangff + * @since 2025/12/9 + */ +@Data +public class VisitRecordSimpleQueryRequest extends PageBasicInfo { + @ApiModelProperty("线索id") + @NotNull(message = "线索id不能为空") + private Long lineId; + + @ApiModelProperty("拜访开始日期") + private Date visitStartDate; + + @ApiModelProperty("拜访结束日期") + private Date visitEndDate; + + @ApiModelProperty(value = "拜访用户id,查询我的拜访时传当前用户", hidden = true) + private String userId; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitSignInRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitSignInRequest.java new file mode 100644 index 000000000..0396604ac --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitSignInRequest.java @@ -0,0 +1,30 @@ +package com.cool.store.request.visit; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + *

+ * 拜访记录签到Request + *

+ * + * @author wangff + * @since 2025/12/8 + */ +@Data +public class VisitSignInRequest { + @ApiModelProperty("线索id") + @NotNull(message = "线索id不能为空") + private Long lineId; + + @ApiModelProperty("签到经纬度,经度,维度") + @NotBlank(message = "签到经纬度不能为空") + private String signInLonLat; + + @ApiModelProperty("签到地址") + @NotBlank(message = "签到地址不能为空") + private String signInAddress; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitSignOutRequest.java b/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitSignOutRequest.java new file mode 100644 index 000000000..0c1d41439 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/request/visit/VisitSignOutRequest.java @@ -0,0 +1,30 @@ +package com.cool.store.request.visit; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + *

+ * 拜访记录签到Request + *

+ * + * @author wangff + * @since 2025/12/8 + */ +@Data +public class VisitSignOutRequest { + @ApiModelProperty("拜访记录id") + @NotNull(message = "拜访记录id不能为空") + private Long id; + + @ApiModelProperty("签到经纬度,经度,维度") + @NotBlank(message = "签到经纬度不能为空") + private String signOutLonLat; + + @ApiModelProperty("签到地址") + @NotBlank(message = "签到地址不能为空") + private String signOutAddress; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/vo/LineInfoVO.java b/coolstore-partner-model/src/main/java/com/cool/store/vo/LineInfoVO.java index 0ca088c4e..8bda3fb0e 100644 --- a/coolstore-partner-model/src/main/java/com/cool/store/vo/LineInfoVO.java +++ b/coolstore-partner-model/src/main/java/com/cool/store/vo/LineInfoVO.java @@ -236,4 +236,6 @@ public class LineInfoVO { private String franchiseBrand; + @ApiModelProperty("被拜访次数") + private Integer visitNum; } diff --git a/coolstore-partner-model/src/main/java/com/cool/store/vo/tp/TpPenaltyApplyDetailVO.java b/coolstore-partner-model/src/main/java/com/cool/store/vo/tp/TpPenaltyApplyDetailVO.java index 236b0ba09..a5c154a51 100644 --- a/coolstore-partner-model/src/main/java/com/cool/store/vo/tp/TpPenaltyApplyDetailVO.java +++ b/coolstore-partner-model/src/main/java/com/cool/store/vo/tp/TpPenaltyApplyDetailVO.java @@ -1,6 +1,7 @@ package com.cool.store.vo.tp; import com.cool.store.annotation.DictField; +import com.cool.store.enums.tp.TpFormStatusEnum; import com.cool.store.enums.tp.TpFormTypeEnum; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -73,6 +74,13 @@ public class TpPenaltyApplyDetailVO { @ApiModelProperty("状态") private String status; + @ApiModelProperty("状态名称") + private String statusName; + + public String getStatusName() { + return TpFormStatusEnum.getMsgByStatus(this.status); + } + public String getTypeName() { return TpFormTypeEnum.getMsgByType(this.type); } diff --git a/coolstore-partner-model/src/main/java/com/cool/store/vo/tp/TpRewardApplyDetailVO.java b/coolstore-partner-model/src/main/java/com/cool/store/vo/tp/TpRewardApplyDetailVO.java index 012ab680e..a134ff2af 100644 --- a/coolstore-partner-model/src/main/java/com/cool/store/vo/tp/TpRewardApplyDetailVO.java +++ b/coolstore-partner-model/src/main/java/com/cool/store/vo/tp/TpRewardApplyDetailVO.java @@ -1,6 +1,7 @@ package com.cool.store.vo.tp; import com.cool.store.annotation.DictField; +import com.cool.store.enums.tp.TpFormStatusEnum; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -61,4 +62,11 @@ public class TpRewardApplyDetailVO { @ApiModelProperty("状态") private String status; + + @ApiModelProperty("状态名称") + private String statusName; + + public String getStatusName() { + return TpFormStatusEnum.getMsgByStatus(this.status); + } } diff --git a/coolstore-partner-model/src/main/java/com/cool/store/vo/visit/VisitPartnerListVO.java b/coolstore-partner-model/src/main/java/com/cool/store/vo/visit/VisitPartnerListVO.java new file mode 100644 index 000000000..f9f9f0d3c --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/vo/visit/VisitPartnerListVO.java @@ -0,0 +1,36 @@ +package com.cool.store.vo.visit; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + *

+ * 拜访加盟商列表VO + *

+ * + * @author wangff + * @since 2025/12/9 + */ +@Data +public class VisitPartnerListVO { + @ApiModelProperty("线索id") + private Long lineId; + + @ApiModelProperty("加盟商姓名") + private String name; + + @ApiModelProperty("加盟商手机号") + private String mobile; + + @ApiModelProperty("开店数量") + private Integer openNum; + + @ApiModelProperty("被拜访次数") + private Integer visitNum; + + @ApiModelProperty("我的拜访次数") + private Integer myVisitNum; + + @ApiModelProperty("新老加盟商,0新 1老") + private Integer isVeteran; +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/vo/visit/VisitRecordDetailVO.java b/coolstore-partner-model/src/main/java/com/cool/store/vo/visit/VisitRecordDetailVO.java new file mode 100644 index 000000000..00526ef73 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/vo/visit/VisitRecordDetailVO.java @@ -0,0 +1,99 @@ +package com.cool.store.vo.visit; + +import com.cool.store.annotation.DictField; +import com.cool.store.enums.visit.VisitStatusEnum; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + *

+ * 拜访记录详情VO + *

+ * + * @author wangff + * @since 2025/12/9 + */ +@Data +public class VisitRecordDetailVO { + @ApiModelProperty("id") + private Long id; + + @ApiModelProperty("加盟商姓名") + private String name; + + @ApiModelProperty("加盟商手机号") + private String mobile; + + @ApiModelProperty("开店数量") + private Integer openNum; + + @ApiModelProperty("被拜访次数") + private Integer visitNum; + + @ApiModelProperty("拜访编号") + private String visitNo; + + @ApiModelProperty("拜访日期") + private Date visitDate; + + @ApiModelProperty("签到地址") + private String signInAddress; + + @ApiModelProperty("签到时间") + private Date signInTime; + + @ApiModelProperty("签退地址") + private String signOutAddress; + + @ApiModelProperty("签退时间") + private Date signOutTime; + + @ApiModelProperty("开新店意愿") + private String desire; + + @ApiModelProperty("开新店意愿名称") + @DictField + private String desireName; + + @ApiModelProperty("意向开店城市") + private String desireCity; + + @ApiModelProperty("意向开店城市名称") + private String desireCityName; + + @ApiModelProperty("具体区域") + private String desireDistrict; + + @ApiModelProperty("具体区域名称") + private String desireDistrictName; + + @ApiModelProperty("是否对应现有铺位") + private Integer existingShopPoint; + + @ApiModelProperty("拜访备注") + private String remark; + + @ApiModelProperty("加盟商合影图片列表") + private String photos; + + @ApiModelProperty("拜访状态") + private Integer status; + + @ApiModelProperty("拜访状态名称") + private String statusName; + + @ApiModelProperty("是否老加盟商") + private Integer isVeteran; + + @ApiModelProperty("拜访人ID") + private String userId; + + @ApiModelProperty("拜访人名称") + private String userName; + + public String getStatusName() { + return VisitStatusEnum.getDescByStatus(status); + } +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/vo/visit/VisitRecordListVO.java b/coolstore-partner-model/src/main/java/com/cool/store/vo/visit/VisitRecordListVO.java new file mode 100644 index 000000000..970e6cd18 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/vo/visit/VisitRecordListVO.java @@ -0,0 +1,79 @@ +package com.cool.store.vo.visit; + +import com.cool.store.enums.visit.VisitStatusEnum; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + *

+ * 拜访记录列表VO + *

+ * + * @author wangff + * @since 2025/12/9 + */ +@Data +public class VisitRecordListVO { + @ApiModelProperty("id") + private Long id; + + @ApiModelProperty(value = "线索id", hidden = true) + private Long lineId; + + @ApiModelProperty("加盟商姓名") + private String name; + + @ApiModelProperty("加盟商手机号") + private String mobile; + + @ApiModelProperty("拜访编号") + private String visitNo; + + @ApiModelProperty("拜访日期") + private Date visitDate; + + @ApiModelProperty("签到地址") + private String signInAddress; + + @ApiModelProperty("开新店意愿") + private String desire; + + @ApiModelProperty("意向开店城市") + private String desireCity; + + @ApiModelProperty("意向开店城市名称") + private String desireCityName; + + @ApiModelProperty("具体区域") + private String desireDistrict; + + @ApiModelProperty("具体区域名称") + private String desireDistrictName; + + @ApiModelProperty("是否对应现有铺位") + private Integer existingShopPoint; + + @ApiModelProperty("加盟商合影图片列表") + private String photos; + + @ApiModelProperty("拜访状态") + private Integer status; + + @ApiModelProperty("拜访状态名称") + private String statusName; + + @ApiModelProperty("新老加盟商,0新 1老") + private Integer isVeteran; + + @ApiModelProperty("拜访人userId") + private String visitUserId; + + @ApiModelProperty("拜访人") + private String visitUserName; + + public String getStatusName() { + return VisitStatusEnum.getDescByStatus(status); + } +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/vo/visit/VisitRecordSimpleListVO.java b/coolstore-partner-model/src/main/java/com/cool/store/vo/visit/VisitRecordSimpleListVO.java new file mode 100644 index 000000000..299a07a33 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/vo/visit/VisitRecordSimpleListVO.java @@ -0,0 +1,52 @@ +package com.cool.store.vo.visit; + +import com.cool.store.enums.visit.VisitStatusEnum; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.Date; + +/** + *

+ * 拜访记录简单信息列表VO + *

+ * + * @author wangff + * @since 2025/12/9 + */ +@Data +public class VisitRecordSimpleListVO { + @ApiModelProperty("拜访记录id") + private Long id; + + @ApiModelProperty(hidden = true) + private Long lineId; + + @ApiModelProperty("加盟商姓名") + private String name; + + @ApiModelProperty("加盟商手机号") + private String mobile; + + @ApiModelProperty("拜访编号") + private String visitNo; + + @ApiModelProperty("拜访日期") + private Date visitDate; + + @ApiModelProperty("拜访状态,0待签到 1待签退 2已完成 3已失效") + private Integer status; + + @ApiModelProperty("拜访状态名称") + private String statusName; + + @ApiModelProperty("拜访人ID") + private String userId; + + @ApiModelProperty("拜访人名称") + private String userName; + + public String getStatusName() { + return VisitStatusEnum.getDescByStatus(status); + } +} diff --git a/coolstore-partner-model/src/main/java/com/cool/store/vo/visit/VisitRecordStatusStatisticsVO.java b/coolstore-partner-model/src/main/java/com/cool/store/vo/visit/VisitRecordStatusStatisticsVO.java new file mode 100644 index 000000000..5e493a553 --- /dev/null +++ b/coolstore-partner-model/src/main/java/com/cool/store/vo/visit/VisitRecordStatusStatisticsVO.java @@ -0,0 +1,27 @@ +package com.cool.store.vo.visit; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + *

+ * 拜访记录状态统计 + *

+ * + * @author wangff + * @since 2026/1/27 + */ +@Data +public class VisitRecordStatusStatisticsVO { + @ApiModelProperty("本月已完成数量") + private Integer monthCompletedCount; + + @ApiModelProperty("已完成数量") + private Integer completedCount; + + @ApiModelProperty("未完成数量") + private Integer uncompletedCount; + + @ApiModelProperty("已失效数量") + private Integer invalidCount; +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/mq/consumer/ConsumerClient.java b/coolstore-partner-service/src/main/java/com/cool/store/mq/consumer/ConsumerClient.java index fd3635272..938ee7513 100644 --- a/coolstore-partner-service/src/main/java/com/cool/store/mq/consumer/ConsumerClient.java +++ b/coolstore-partner-service/src/main/java/com/cool/store/mq/consumer/ConsumerClient.java @@ -10,6 +10,7 @@ import com.cool.store.enums.RocketMqGroupEnum; import com.cool.store.mq.RocketMqConfig; import com.cool.store.mq.consumer.listener.ShopDecorationAssignListener; import com.cool.store.mq.consumer.listener.StoreUserUpdateListener; +import com.cool.store.mq.consumer.listener.VisitRecordListener; import com.cool.store.mq.consumer.listener.XfsgTrainingPersonSyncListener; import com.google.common.collect.Maps; import org.springframework.context.annotation.Bean; @@ -38,6 +39,8 @@ public class ConsumerClient { private StoreUserUpdateListener storeUserUpdateListener; @Resource private ShopDecorationAssignListener shopDecorationAssignListener; + @Resource + private VisitRecordListener visitRecordListener; /** * 获取通用配置 @@ -117,4 +120,17 @@ public class ConsumerClient { return consumerBean; } + @Bean(initMethod = "start", destroyMethod = "shutdown") + public ConsumerBean visitRecordInvalid() { + RocketMqGroupEnum groupEnum = RocketMqGroupEnum.VISIT_RECORD_INVALID; + ConsumerBean consumerBean = new ConsumerBean(); + //配置文件 + Properties properties = getCommonProperties(groupEnum); + consumerBean.setProperties(properties); + Map commonSubscriptionTable = getCommonSubscriptionTable(groupEnum, visitRecordListener); + //订阅多个topic如上面设置 + consumerBean.setSubscriptionTable(commonSubscriptionTable); + return consumerBean; + } + } diff --git a/coolstore-partner-service/src/main/java/com/cool/store/mq/consumer/listener/VisitRecordListener.java b/coolstore-partner-service/src/main/java/com/cool/store/mq/consumer/listener/VisitRecordListener.java new file mode 100644 index 000000000..ee7c6cb4c --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/mq/consumer/listener/VisitRecordListener.java @@ -0,0 +1,54 @@ +package com.cool.store.mq.consumer.listener; + +import com.aliyun.openservices.ons.api.Action; +import com.aliyun.openservices.ons.api.ConsumeContext; +import com.aliyun.openservices.ons.api.Message; +import com.aliyun.openservices.ons.api.MessageListener; +import com.cool.store.constants.CommonConstants; +import com.cool.store.service.visit.VisitRecordService; +import com.cool.store.utils.RedisUtilPool; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/** + *

+ * 拜访记录失效listener + *

+ * + * @author wangff + * @since 2026/1/26 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class VisitRecordListener implements MessageListener { + private final RedisUtilPool redisUtilPool; + private final VisitRecordService visitRecordService; + + + @Override + public Action consume(Message message, ConsumeContext consumeContext) { + String text = new String(message.getBody()); + if (StringUtils.isBlank(text)) { + log.info("消息体为空,tag:{},messageId:{}", message.getTag(), message.getMsgID()); + return Action.CommitMessage; + } + String lockKey = "VisitRecordListener:" + message.getMsgID(); + boolean lock = redisUtilPool.setNxExpire(lockKey, message.getMsgID(), CommonConstants.NORMAL_LOCK_TIMES); + if (lock) { + try { + visitRecordService.recordInvalid(Long.valueOf(text)); + } catch (Exception e) { + log.error("VisitRecordListener consume error", e); + return Action.ReconsumeLater; + } finally { + redisUtilPool.delKey(lockKey); + } + log.info("消费成功,tag:{},messageId:{},reqBody={}", message.getTag(), message.getMsgID(), text); + return Action.CommitMessage; + } + return Action.ReconsumeLater; + } +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/UserAuthMappingService.java b/coolstore-partner-service/src/main/java/com/cool/store/service/UserAuthMappingService.java index 45eb41a97..988140566 100644 --- a/coolstore-partner-service/src/main/java/com/cool/store/service/UserAuthMappingService.java +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/UserAuthMappingService.java @@ -34,6 +34,8 @@ public interface UserAuthMappingService { */ List getAuthRegionIdAndSubRegionIdByUserId(String userId); + List getAuthRegionIdAndSubRegionIdExcludeStoreByUserId(String userId); + /** * 获取所有管辖下的区域ID 不包含门店 * @param userId diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/impl/LineServiceImpl.java b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/LineServiceImpl.java index 0f8c7f653..ccb6c2057 100644 --- a/coolstore-partner-service/src/main/java/com/cool/store/service/impl/LineServiceImpl.java +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/LineServiceImpl.java @@ -6,6 +6,7 @@ import com.cool.store.constants.CommonConstants; import com.cool.store.constants.RedisConstant; import com.cool.store.context.LoginUserInfo; import com.cool.store.dao.*; +import com.cool.store.dao.visit.VisitRecordDAO; import com.cool.store.entity.*; import com.cool.store.enums.*; import com.cool.store.enums.point.PayBusinessTypeEnum; @@ -91,6 +92,8 @@ public class LineServiceImpl implements LineService { QualificationsInfoDAO qualificationsInfoDAO; @Resource EnterpriseService enterpriseService; + @Resource + VisitRecordDAO visitRecordDAO; @Override @@ -186,6 +189,7 @@ public class LineServiceImpl implements LineService { } } result.setDevelopmentManager(developmentManagerName.toString()); + result.setVisitNum(visitRecordDAO.getCompleteVisitNum(result.getId())); return result; } diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/impl/UserAuthMappingServiceImpl.java b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/UserAuthMappingServiceImpl.java index ddc34b349..1068c3b65 100644 --- a/coolstore-partner-service/src/main/java/com/cool/store/service/impl/UserAuthMappingServiceImpl.java +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/impl/UserAuthMappingServiceImpl.java @@ -119,6 +119,20 @@ public class UserAuthMappingServiceImpl implements UserAuthMappingService { } + @Override + public List getAuthRegionIdAndSubRegionIdExcludeStoreByUserId(String userId) { + List userAuthMapping = listUserAuthMappingByUserId(userId); + if (CollectionUtils.isEmpty(userAuthMapping)) { + return Lists.newArrayList(); + } + List regionIds = userAuthMapping.stream().map(UserAuthMappingDO::getMappingId).collect(Collectors.toList()); + List subRegionIds = regionMapper.getSubRegionIdsExcludeStore(regionIds); + if (CollectionUtils.isNotEmpty(subRegionIds)) { + regionIds.addAll(subRegionIds); + } + return regionIds; + } + @Override public List getAuthStoreIdAndSubRegionIdByUserId(String userId) { List userAuthMapping = listUserAuthMappingByUserId(userId); diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/visit/VisitRecordService.java b/coolstore-partner-service/src/main/java/com/cool/store/service/visit/VisitRecordService.java new file mode 100644 index 000000000..dbc73a3cc --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/visit/VisitRecordService.java @@ -0,0 +1,84 @@ +package com.cool.store.service.visit; + +import com.cool.store.request.visit.*; +import com.cool.store.vo.visit.*; +import com.github.pagehelper.PageInfo; + +/** + *

+ * 拜访记录Service + *

+ * + * @author wangff + * @since 2025/12/8 + */ +public interface VisitRecordService { + + /** + * 签到 + * @param request 拜访记录签到Request + * @return 拜访记录id + */ + String signIn(VisitSignInRequest request); + + /** + * 签退 + * @param request 拜访记录签到Request + * @return 拜访记录id + */ + String signOut(VisitSignOutRequest request); + + /** + * 编辑拜访记录 + * @param request 拜访记录编辑Request + * @return 是否成功 + */ + Boolean editRecord(VisitEditRequest request); + + /** + * 拜访加盟商列表 + * @param request 拜访加盟商查询Request + * @return 拜访加盟商列表VO + */ + PageInfo visitPartnerList(VisitPartnerQueryRequest request); + + /** + * 查询拜访记录简单信息 + * @param request 拜访记录查询Request + * @return 拜访记录列表VO + */ + PageInfo recordSimpleList(VisitRecordSimpleQueryRequest request); + + /** + * 获取拜访记录详情 + * @param id 拜访记录id + * @return 拜访记录详情VO + */ + VisitRecordDetailVO getDetail(Long id); + + /** + * 取消拜访 + * @param request 拜访记录取消Request + * @return 是否成功 + */ + Boolean cancelVisit(VisitCancelRequest request); + + /** + * 获取拜访记录列表 + * @param request 拜访记录查询Request + * @return 拜访记录列表VO + */ + PageInfo recordList(VisitRecordQueryRequest request); + + /** + * 记录失效 + * @param id id + */ + void recordInvalid(Long id); + + /** + * 拜访记录状态统计 + * @return 拜访记录状态统计VO + */ + VisitRecordStatusStatisticsVO countStatusStatistics(VisitRecordQueryRequest request); +} diff --git a/coolstore-partner-service/src/main/java/com/cool/store/service/visit/impl/VisitRecordServiceImpl.java b/coolstore-partner-service/src/main/java/com/cool/store/service/visit/impl/VisitRecordServiceImpl.java new file mode 100644 index 000000000..76db92a64 --- /dev/null +++ b/coolstore-partner-service/src/main/java/com/cool/store/service/visit/impl/VisitRecordServiceImpl.java @@ -0,0 +1,289 @@ +package com.cool.store.service.visit.impl; + +import cn.hutool.core.collection.CollStreamUtil; +import com.cool.store.constants.CommonConstants; +import com.cool.store.context.CurrentUserHolder; +import com.cool.store.dao.*; +import com.cool.store.dao.ad.AdDistrictDAO; +import com.cool.store.dao.store.StoreMasterSignerInfoDAO; +import com.cool.store.dao.visit.VisitRecordDAO; +import com.cool.store.dto.region.BigRegionDTO; +import com.cool.store.entity.LineInfoDO; +import com.cool.store.entity.QualificationsInfoDO; +import com.cool.store.entity.StoreDO; +import com.cool.store.entity.store.StoreMasterSignerInfoDO; +import com.cool.store.entity.visit.VisitRecordDO; +import com.cool.store.enums.ErrorCodeEnum; +import com.cool.store.enums.RocketMqTagEnum; +import com.cool.store.enums.visit.VisitStatusEnum; +import com.cool.store.exception.ServiceException; +import com.cool.store.mq.producer.SimpleMessageService; +import com.cool.store.request.visit.*; +import com.cool.store.service.SysRoleService; +import com.cool.store.service.UserAuthMappingService; +import com.cool.store.service.dict.impl.DictService; +import com.cool.store.service.visit.VisitRecordService; +import com.cool.store.utils.BeanUtil; +import com.cool.store.utils.poi.StringUtils; +import com.cool.store.vo.visit.*; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + *

+ * 拜访记录服务实现类 + *

+ * + * @author wangff + * @since 2025/12/8 + */ +@Service +@RequiredArgsConstructor +public class VisitRecordServiceImpl implements VisitRecordService { + private final VisitRecordDAO visitRecordDAO; + private final UserAuthMappingService userAuthMappingService; + private final QualificationsInfoDAO qualificationsInfoDAO; + private final StoreDao storeDao; + private final StoreMasterSignerInfoDAO storeSignerDAO; + private final LineInfoDAO lineInfoDAO; + private final BigRegionDAO bigRegionDAO; + private final DictService dictService; + private final SysRoleService sysRoleService; + private final SimpleMessageService simpleMessageService; + private final AdDistrictDAO adDistrictDAO; + private final EnterpriseUserDAO enterpriseUserDAO; + + @Override + public String signIn(VisitSignInRequest request) { + VisitRecordDO record = VisitRecordDO.builder() + .visitNo(generateVisitNo()) + .lineId(request.getLineId()) + .signInLonLat(request.getSignInLonLat()) + .signInAddress(request.getSignInAddress()) + .status(VisitStatusEnum.N_SIGN_OUT.getStatus()) + .signInTime(new Date()) + .userId(CurrentUserHolder.getUserId()) + .build(); + visitRecordDAO.insertSelective(record); + simpleMessageService.send(String.valueOf(record.getId()), RocketMqTagEnum.VISIT_RECORD_INVALID, System.currentTimeMillis() + 12 * 60 * 60 * 1000); + return record.getId().toString(); + } + + @Override + public String signOut(VisitSignOutRequest request) { + VisitRecordDO old = visitRecordDAO.getById(request.getId()); + verify(old); + if (CommonConstants.INDEX_ZERO.equals(old.getCompleteEdit())) { + throw new ServiceException(ErrorCodeEnum.VISIT_RECORD_NOT_COMPLETE_EDIT); + } + VisitRecordDO record = VisitRecordDO.builder() + .id(request.getId()) + .signOutTime(new Date()) + .signOutLonLat(request.getSignOutLonLat()) + .signOutAddress(request.getSignOutAddress()) + .status(VisitStatusEnum.COMPLETE.getStatus()) + .build(); + visitRecordDAO.updateSelective(record); + return record.getId().toString(); + } + + @Override + public Boolean editRecord(VisitEditRequest request) { + VisitRecordDO old = visitRecordDAO.getById(request.getId()); + verify(old); + VisitRecordDO record = BeanUtil.toBean(request, VisitRecordDO.class); + record.setCompleteEdit(CommonConstants.INDEX_ONE); + return visitRecordDAO.updateSelective(record); + } + + @Override + public PageInfo visitPartnerList(VisitPartnerQueryRequest request) { + List authRegionIds = null; + String currentUserId = CurrentUserHolder.getUserId(); + if (!sysRoleService.checkIsAdmin(currentUserId)) { + authRegionIds = userAuthMappingService.getAuthRegionIdAndSubRegionIdExcludeStoreByUserId(currentUserId) + .stream() + .map(Long::valueOf) + .collect(Collectors.toList()); + List bigRegionList = bigRegionDAO.queryAllBigRegion(null, null, null); + Set bigRegionIds = CollStreamUtil.toSet(bigRegionList, BigRegionDTO::getRegionId); + authRegionIds.retainAll(bigRegionIds); + if (CollectionUtils.isEmpty(authRegionIds)) { + return new PageInfo<>(); + } + } + PageHelper.startPage(request.getPageNum(), request.getPageSize()); + List lineIds = visitRecordDAO.getPartnerLineId(authRegionIds, request.getKeyword(), request.getUserId()); + PageInfo page = new PageInfo<>(lineIds); + List list = visitRecordDAO.getVisitRecordListByLineIds(lineIds, request.getUserId()); + PageInfo result = BeanUtil.toPage(page, VisitPartnerListVO.class); + result.setList(list); + + List lineList = lineInfoDAO.getByLineIds(lineIds); + Map lineMap = CollStreamUtil.toMap(lineList, LineInfoDO::getId, v -> v); + Map storeNumMap = getStoreNumMap(lineIds); + list.forEach(v -> { + v.setOpenNum(storeNumMap.getOrDefault(v.getLineId(), 0)); + v.setIsVeteran(v.getOpenNum() > 0 ? 1 : 0); + LineInfoDO lineInfoDO = lineMap.get(v.getLineId()); + if (Objects.nonNull(lineInfoDO)) { + v.setName(lineInfoDO.getUsername()); + v.setMobile(lineInfoDO.getMobile()); + } + }); + return result; + } + + private Map getStoreNumMap(List lineIds) { + List qualificationList = qualificationsInfoDAO.getByLineIds(lineIds); + Map idCardMap = CollStreamUtil.toMap(qualificationList, QualificationsInfoDO::getLineId, QualificationsInfoDO::getIdCardNo); + // 根据身份证查询对应签约人开店数量 + List storeSignerList = storeSignerDAO.getListByIdCard(idCardMap.values()); + List storeIds = CollStreamUtil.toList(storeSignerList, StoreMasterSignerInfoDO::getStoreId); + List effectiveStoreList = storeDao.getEffectiveStoreByStoreIds(storeIds); + Set effectiveStoreIds = CollStreamUtil.toSet(effectiveStoreList, StoreDO::getStoreId); + + Map storeNumMap = new HashMap<>(); + for (StoreMasterSignerInfoDO signerInfoDO : storeSignerList) { + if (effectiveStoreIds.contains(signerInfoDO.getStoreId())) { + storeNumMap.compute(signerInfoDO.getSigner1IdCardNo(), (k, v) -> Objects.isNull(v) ? 1 : v + 1); + storeNumMap.compute(signerInfoDO.getSigner2IdCardNo(), (k, v) -> Objects.isNull(v) ? 1 : v + 1); + } + } + return CollStreamUtil.toMap(lineIds, v -> v, v -> storeNumMap.getOrDefault(idCardMap.get(v), 0)); + } + + @Override + public PageInfo recordSimpleList(VisitRecordSimpleQueryRequest request) { + PageHelper.startPage(request.getPageNum(), request.getPageSize()); + List list = visitRecordDAO.getByLineId(request.getLineId(), request.getVisitStartDate(), request.getVisitEndDate(), request.getUserId()); + PageInfo page = new PageInfo<>(list); + Set lineIds = CollStreamUtil.toSet(list, VisitRecordDO::getLineId); + Set userIdList = CollStreamUtil.toSet(list, VisitRecordDO::getUserId); + Map userNameMap = new HashMap<>(); + if (CollectionUtils.isNotEmpty(userIdList)){ + userNameMap = enterpriseUserDAO.getUserNameMap(new ArrayList<>(userIdList)); + } + List lineList = lineInfoDAO.getByLineIds(new ArrayList<>(lineIds)); + Map lineMap = CollStreamUtil.toMap(lineList, LineInfoDO::getId, v -> v); + PageInfo result = BeanUtil.toPage(page, VisitRecordSimpleListVO.class); + Map finalUserNameMap = userNameMap; + result.getList().forEach(v -> { + LineInfoDO lineInfoDO = lineMap.get(v.getLineId()); + String userName = finalUserNameMap.getOrDefault(v.getUserId(), ""); + v.setUserName(userName); + if (Objects.nonNull(lineInfoDO)) { + v.setName(lineInfoDO.getUsername()); + v.setMobile(lineInfoDO.getMobile()); + } + }); + return result; + } + + @Override + public VisitRecordDetailVO getDetail(Long id) { + VisitRecordDO record = visitRecordDAO.getById(id); + if (Objects.isNull(record)) { + throw new ServiceException(ErrorCodeEnum.VISIT_RECORD_NOT_EXIST); + } + VisitRecordDetailVO vo = BeanUtil.toBean(record, VisitRecordDetailVO.class); + LineInfoDO lineInfo = lineInfoDAO.getLineInfo(record.getLineId()); + if (Objects.nonNull(lineInfo)) { + vo.setName(lineInfo.getUsername()); + vo.setMobile(lineInfo.getMobile()); + } + if (StringUtils.isNotBlank(record.getUserId())){ + String userName = enterpriseUserDAO.getUserName(record.getUserId()); + vo.setUserId(record.getUserId()); + vo.setUserName(userName); + } + Map storeNumMap = getStoreNumMap(Collections.singletonList(record.getLineId())); + vo.setOpenNum(storeNumMap.getOrDefault(record.getLineId(), 0)); + vo.setIsVeteran(storeNumMap.getOrDefault(record.getLineId(), 0) > 0 ? 1 : 0); + List list = visitRecordDAO.getVisitRecordListByLineIds(Collections.singletonList(record.getLineId()), null); + Map adNameMap = adDistrictDAO.getNameByCodes(Arrays.asList(vo.getDesireCity(), vo.getDesireDistrict())); + if (CollectionUtils.isNotEmpty(list)) { + VisitPartnerListVO partner = list.get(0); + vo.setVisitNum(partner.getVisitNum()); + vo.setDesireCityName(adNameMap.get(vo.getDesireCity())); + vo.setDesireDistrictName(adNameMap.get(vo.getDesireDistrict())); + } + dictService.fillDictField(vo); + return vo; + } + + @Override + public Boolean cancelVisit(VisitCancelRequest request) { + return visitRecordDAO.deleteById(request.getId()); + } + + @Override + public PageInfo recordList(VisitRecordQueryRequest request) { + PageHelper.startPage(request.getPageNum(), request.getPageSize()); + List list = visitRecordDAO.getVisitRecordList(request); + PageInfo page = new PageInfo<>(list); + Set lineIds = CollStreamUtil.toSet(list, VisitRecordListVO::getLineId); + Set userIdList = CollStreamUtil.toSet(list, VisitRecordListVO::getVisitUserId); + Map userNameMap = new HashMap<>(); + if (CollectionUtils.isNotEmpty(userIdList)){ + userNameMap = enterpriseUserDAO.getUserNameMap(new ArrayList<>(userIdList)); + } + List adCodes = list.stream() + .flatMap(v -> Stream.of(v.getDesireCity(), v.getDesireDistrict())) + .distinct() + .collect(Collectors.toList()); + Map adNameMap = adDistrictDAO.getNameByCodes(adCodes); + Map storeNumMap = getStoreNumMap(new ArrayList<>(lineIds)); + Map finalUserNameMap = userNameMap; + list.forEach(v -> { + v.setIsVeteran(storeNumMap.getOrDefault(v.getLineId(), 0) > 0 ? 1 : 0); + v.setDesireCityName(adNameMap.get(v.getDesireCity())); + v.setDesireDistrictName(adNameMap.get(v.getDesireDistrict())); + v.setVisitUserName(finalUserNameMap.get(v.getVisitUserId())); + }); + return page; + } + + @Override + public void recordInvalid(Long id) { + VisitRecordDO record = visitRecordDAO.getById(id); + if (Objects.nonNull(record) && VisitStatusEnum.N_SIGN_OUT.getStatus().equals(record.getStatus())) { + VisitRecordDO update = VisitRecordDO.builder().id(record.getId()).status(VisitStatusEnum.INVALID.getStatus()).build(); + visitRecordDAO.updateSelective(update); + } + } + + @Override + public VisitRecordStatusStatisticsVO countStatusStatistics(VisitRecordQueryRequest request) { + return visitRecordDAO.countStatusStatistics(request); + } + + /** + * 校验操作权限及拜访状态 + */ + private void verify(VisitRecordDO record) { + if (Objects.isNull(record)) { + throw new ServiceException(ErrorCodeEnum.VISIT_RECORD_NOT_EXIST); + } + if (VisitStatusEnum.INVALID.getStatus().equals(record.getStatus())) { + throw new ServiceException(ErrorCodeEnum.VISIT_RECORD_INVALID); + } + if (VisitStatusEnum.COMPLETE.getStatus().equals(record.getStatus())) { + throw new ServiceException(ErrorCodeEnum.VISIT_RECORD_COMPLETE); + } + if (!record.getUserId().equals(CurrentUserHolder.getUserId())) { + throw new ServiceException(ErrorCodeEnum.VISIT_RECORD_NOT_YOUR_OWN); + } + } + + private String generateVisitNo() { + return "V" + System.currentTimeMillis(); + } +} diff --git a/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/VisitRecordController.java b/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/VisitRecordController.java new file mode 100644 index 000000000..39606bd29 --- /dev/null +++ b/coolstore-partner-web/src/main/java/com/cool/store/controller/webb/VisitRecordController.java @@ -0,0 +1,103 @@ +package com.cool.store.controller.webb; + +import com.cool.store.context.CurrentUserHolder; +import com.cool.store.request.visit.*; +import com.cool.store.response.ResponseResult; +import com.cool.store.service.visit.VisitRecordService; +import com.cool.store.vo.visit.*; +import com.github.pagehelper.PageInfo; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; + +/** + *

+ * 拜访记录 前端控制器 + *

+ * + * @author wangff + * @since 2025/12/9 + */ +@Api(tags = "拜访记录") +@RestController +@RequestMapping("/pc/visit/record") +@RequiredArgsConstructor +public class VisitRecordController { + + private final VisitRecordService visitRecordService; + + @ApiOperation("签到") + @PostMapping("/signIn") + public ResponseResult signIn(@RequestBody @Validated VisitSignInRequest request) { + return ResponseResult.success(visitRecordService.signIn(request)); + } + + @ApiOperation("签退") + @PostMapping("/signOut") + public ResponseResult signOut(@RequestBody @Validated VisitSignOutRequest request) { + return ResponseResult.success(visitRecordService.signOut(request)); + } + + @ApiOperation("编辑拜访记录") + @PostMapping("/editRecord") + public ResponseResult editRecord(@RequestBody @Validated VisitEditRequest request) { + return ResponseResult.success(visitRecordService.editRecord(request)); + } + + @ApiOperation("拜访加盟商列表") + @PostMapping("/partnerList") + public ResponseResult> visitPartnerList(@RequestBody VisitPartnerQueryRequest request) { + return ResponseResult.success(visitRecordService.visitPartnerList(request)); + } + + @ApiOperation("我的拜访加盟商列表") + @PostMapping("/personPartnerList") + public ResponseResult> personVisitPartnerList(@RequestBody VisitPartnerQueryRequest request) { + request.setUserId(CurrentUserHolder.getUserId()); + return ResponseResult.success(visitRecordService.visitPartnerList(request)); + } + + @ApiOperation("查询拜访记录简单信息(h5使用)") + @PostMapping("/simpleList") + public ResponseResult> recordSimpleList(@RequestBody VisitRecordSimpleQueryRequest request) { + request.setUserId(CurrentUserHolder.getUserId()); + return ResponseResult.success(visitRecordService.recordSimpleList(request)); + } + + @ApiOperation("我的查询拜访记录简单信息(h5使用)") + @PostMapping("/personSimpleList") + public ResponseResult> personRecordSimpleList(@RequestBody VisitRecordSimpleQueryRequest request) { + request.setUserId(CurrentUserHolder.getUserId()); + return ResponseResult.success(visitRecordService.recordSimpleList(request)); + } + + @ApiOperation("获取拜访记录详情") + @GetMapping("/detail") + @ApiImplicitParam(name = "id", value = "拜访记录id", required = true, dataType = "Long", paramType = "query") + public ResponseResult getDetail(@NotNull(message = "拜访记录id不能为空") Long id) { + return ResponseResult.success(visitRecordService.getDetail(id)); + } + + @ApiOperation("取消拜访") + @PostMapping("/cancel") + public ResponseResult cancelVisit(@RequestBody @Validated VisitCancelRequest request) { + return ResponseResult.success(visitRecordService.cancelVisit(request)); + } + + @ApiOperation("获取拜访记录列表") + @PostMapping("/list") + public ResponseResult> recordList(@RequestBody VisitRecordQueryRequest request) { + return ResponseResult.success(visitRecordService.recordList(request)); + } + + @ApiOperation("拜访记录状态统计") + @PostMapping("/countStatusStatistics") + public ResponseResult countStatusStatistics(@RequestBody VisitRecordQueryRequest request) { + return ResponseResult.success(visitRecordService.countStatusStatistics(request)); + } +} \ No newline at end of file