Преглед на файлове

微信客服三期;广告平台跳转

zoujiajian преди 1 година
родител
ревизия
f599dd2770

+ 13 - 0
netflix-dao/src/main/java/com/cyksj/mapper/CustomerConsultationRecordMapper.java

@@ -0,0 +1,13 @@
+package com.cyksj.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.CustomerConsultationRecord;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: CustomerConsultationRecordMapper
+ * 创建者: JavaZou
+ * 创建时间:2025/2/7 11:59
+ */
+public interface CustomerConsultationRecordMapper extends BaseMapper<CustomerConsultationRecord> {
+}

+ 30 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CustomerConsultationRecord.java

@@ -0,0 +1,30 @@
+package com.cyksj.model.entity;
+
+
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.Setter;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotEmpty;
+
+@Getter
+@Setter
+public class CustomerConsultationRecord extends BaseEntity{
+	private Long userId;
+
+	@NonNull
+	private Integer type;
+
+	@NonNull
+	private Long goodsId;
+
+	@NonNull
+	private String categoryName;
+
+	@NotEmpty
+	@Length(max = 1000)
+	private String content;
+
+	private String imgs;
+}

+ 24 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/HomeManagementConfig.java

@@ -62,6 +62,30 @@ public class HomeManagementConfig extends BaseEntity{
 	@TableField(updateStrategy = FieldStrategy.IGNORED)
 	private String rgSubTitle;
 
+	/**
+	 * 跳转类型
+	 */
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private Integer jumpType;
+
+	/**
+	 * 跳转平台
+	 */
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private String platSkuIds;
+
+	/**
+	 * 跳转平台
+	 */
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private String jumpTitle;
+
+	/**
+	 * 跳转规格
+	 */
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private String jumpSpecVal;
+
 	private Boolean deleted;
 
 	@Getter

+ 22 - 0
netflix-dao/src/main/java/com/cyksj/model/request/HomeManageConfigReq.java

@@ -47,4 +47,26 @@ public class HomeManageConfigReq {
 	private Integer sorted;
 
 	private String tag;
+
+	/**
+	 * 跳转类型
+	 */
+	private Integer jumpType = 2;
+
+	/**
+	 * 跳转平台
+	 */
+	private String platSkuIds;
+
+	/**
+	 * 跳转平台
+	 */
+	private String jumpTitle;
+
+	/**
+	 * 跳转规格
+	 */
+	private String jumpSpecVal;
+
+	private Boolean status;
 }

+ 57 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CustomerConsultationRecordView.java

@@ -0,0 +1,57 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: CustomerConsultationRecordView
+ * 创建者: JavaZou
+ * 创建时间:2025/2/7 15:07
+ */
+@Getter
+@Setter
+@SearchBean(tables = "customer_consultation_record ccr left join goods_don g on g.id = ccr.goods_id" +
+		" left join user u on u.id = ccr.user_id")
+public class CustomerConsultationRecordView {
+
+	@DbField("ccr.id")
+	private Long id;
+
+	@DbField("ccr.user_id")
+	private Long userId;
+
+	@DbField("u.nickname")
+	private String nickname;
+
+	@DbField("g.title")
+	private String title;
+
+	@DbField("g.id")
+	private Long goodsId;
+
+	@DbField("ccr.type")
+	private Integer type;
+
+	/**
+	 * 分类名
+	 */
+	@DbField("ccr.category_name")
+	private String categoryName;
+
+	@DbField("ccr.content")
+	private String content;
+
+	@DbField("ccr.imgs")
+	private String imgs;
+
+	@DbField("ccr.created_time")
+	private Date createdTime;
+
+	@DbField("ccr.update_time")
+	private Date updateTime;
+}

+ 28 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CustomerConsultationStatisticsDataView.java

@@ -0,0 +1,28 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: CustomerConsultationStatisticsDataView
+ * 创建者: JavaZou
+ * 创建时间:2025/2/7 15:17
+ */
+@Getter
+@Setter
+@SearchBean(tables = "customer_consultation_record ccr left join goods_don g on g.id = ccr.goods_id",
+		where = ":time:",
+		groupBy = "ccr.type,ccr.category_name,g.title")
+public class CustomerConsultationStatisticsDataView {
+	@DbField("ccr.type")
+	private Integer type;
+
+	@DbField("ccr.category_name")
+	private String categoryName;
+
+	@DbField("g.title")
+	private String title;
+}

+ 75 - 0
netflix-dao/src/main/java/com/cyksj/model/views/GoodsQaShowView.java

@@ -0,0 +1,75 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: GoodsQaShowView
+ * 创建者: JavaZou
+ * 创建时间:2025/2/7 12:04
+ */
+@Getter
+@Setter
+@SearchBean(tables = "goods_don_qa_relate gr inner join goods_don_qa ga on ga.id = gr.question_id" +
+		" inner join goods_don g on g.id = gr.goods_id" +
+		" left join goods_don_category_relate gcr on gcr.goods_id = g.id" +
+		" left join goods_don_category gc on gc.id = gcr.category",
+		where = "ga.status is true and ga.deleted is true and g.status is true")
+public class GoodsQaShowView {
+	/**
+	 * 1.售前,2.售后
+	 */
+	@DbField("ga.type")
+	private Integer type;
+
+	@DbIgnore
+	private String label;
+
+	@DbIgnore
+	private Integer value;
+
+	@DbField("g.id")
+	private Long goodsId;
+
+	@DbField("g.title")
+	private String title;
+
+	/**
+	 * 分类
+	 */
+	@DbField("gc.name")
+	private String categoryName;
+
+	@DbIgnore
+	private List<QaGoodsType> children;
+
+	@Getter
+	@Setter
+	public static class QaGoodsType {
+		private Long category;
+
+		//分类名
+		private String label;
+
+		//分类名
+		private String value;
+
+		private List<GoodsDonQaView> children;
+
+		@Getter
+		@Setter
+		public static class GoodsDonQaView{
+			//平台名
+			private String label;
+
+			//平台id
+			private Long value;
+		}
+	}
+}

+ 27 - 0
netflix-dao/src/main/java/com/cyksj/model/views/HomeManagementConfigView.java

@@ -75,6 +75,33 @@ public class HomeManagementConfigView {
 	@DbField("hc.time")
 	private Long time;
 
+	/**
+	 * 跳转类型 1.商品 2链接
+	 */
+	@DbField("hc.jump_type")
+	private Integer jumpType;
+
+	/**
+	 * 跳转平台
+	 */
+	@DbField("hc.plat_sku_ids")
+	private String platSkuIds;
+
+	/**
+	 * 跳转平台
+	 */
+	@DbField("hc.jump_title")
+	private String jumpTitle;
+
+	/**
+	 * 跳转规格
+	 */
+	@DbField("hc.jump_spec_val")
+	private String jumpSpecVal;
+
 	@DbField("hc.created_time")
 	private Date createdTime;
+
+	@DbField("hc.update_time")
+	private Date updateTime;
 }

+ 23 - 0
netflix-dao/src/main/java/com/cyksj/model/views/HomeManagementFrontView.java

@@ -72,6 +72,29 @@ public class HomeManagementFrontView {
 	@DbField("hc.tag")
 	private String tag;
 
+	/**
+	 * 跳转类型
+	 */
+	@DbField("hc.jump_type")
+	private Integer jumpType;
+
+	/**
+	 * 跳转平台
+	 */
+	@DbField("hc.plat_sku_ids")
+	private String platSkuIds;
+
+	/**
+	 * 跳转平台
+	 */
+	@DbField("hc.jump_title")
+	private String jumpTitle;
+
+	/**
+	 * 跳转规格
+	 */
+	@DbField("hc.jump_spec_val")
+	private String jumpSpecVal;
 	/**
 	 * 商品标签
 	 */

+ 78 - 0
netflix-web/src/main/java/com/cyksj/web/controller/corp/CorpKfFrontController.java

@@ -0,0 +1,78 @@
+package com.cyksj.web.controller.corp;
+
+
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.mapper.corp.kf.CorpKfAccountFollowMapper;
+import com.cyksj.mapper.corp.kf.CorpKfAccountMapper;
+import com.cyksj.model.entity.CorpKfAccount;
+import com.cyksj.model.entity.CorpKfAccountFollow;
+import com.cyksj.redis.RedisService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/applet/coupon")
+@RequiredArgsConstructor
+public class CorpKfFrontController {
+	private final CorpKfAccountMapper corpKfAccountMapper;
+
+	private final CorpKfAccountFollowMapper corpKfAccountFollowMapper;
+
+	private final RedisService redisService;
+
+	/**
+	 * 当前微信客服是否处于值班
+	 */
+	@GetMapping("/get/duty")
+	public Result<Boolean> isDuty(@RequestParam(defaultValue = "1") Integer scene) {
+		CorpKfAccount corpKfAccount = corpKfAccountMapper.selectOne(Wrappers.lambdaQuery(CorpKfAccount.class).eq(CorpKfAccount::getScene, scene).last("limit 1"));
+		//默认官网
+		if (corpKfAccount == null) {
+			corpKfAccount = corpKfAccountMapper.selectOne(Wrappers.lambdaQuery(CorpKfAccount.class).eq(CorpKfAccount::getScene, 1).last("limit 1"));
+			if (corpKfAccount == null) {
+				return GatewayResponse.SUCCESS.newBuilder().toResult(false);
+			}
+		}
+		DateTime now = DateTime.now();
+		String thisHourDate = DateUtil.format(now, "HH:mm");
+		//星期天为1
+		int dayOfWeek = DateUtil.dayOfWeek(now);
+		//是否值班期
+		List<CorpKfAccountFollow> corpKfAccountFollows = corpKfAccountFollowMapper.selectList(Wrappers.lambdaQuery(CorpKfAccountFollow.class)
+				.eq(CorpKfAccountFollow::getOpenKfId, corpKfAccount.getOpenKfId())
+				.le(CorpKfAccountFollow::getStartTime, thisHourDate)
+				.ge(CorpKfAccountFollow::getEndTime, thisHourDate)
+				.eq(CorpKfAccountFollow::getDutyWeek, dayOfWeek));
+		return GatewayResponse.SUCCESS.newBuilder().toResult(!corpKfAccountFollows.isEmpty());
+	}
+
+
+	/**
+	 * 值班情况
+	 */
+	@GetMapping("/get/duty/info")
+	public Result<List<CorpKfAccountFollow>> dutyInfo(@RequestParam(defaultValue = "1") Integer scene) {
+		CorpKfAccount corpKfAccount = corpKfAccountMapper.selectOne(Wrappers.lambdaQuery(CorpKfAccount.class).eq(CorpKfAccount::getScene, scene).last("limit 1"));
+		//默认官网
+		if (corpKfAccount == null) {
+			corpKfAccount = corpKfAccountMapper.selectOne(Wrappers.lambdaQuery(CorpKfAccount.class).eq(CorpKfAccount::getScene, 1).last("limit 1"));
+			if (corpKfAccount == null) {
+				return GatewayResponse.SUCCESS.newBuilder().toResult();
+			}
+		}
+		//是否值班期
+		List<CorpKfAccountFollow> corpKfAccountFollows = corpKfAccountFollowMapper.selectList(Wrappers.lambdaQuery(CorpKfAccountFollow.class)
+				.eq(CorpKfAccountFollow::getOpenKfId, corpKfAccount.getOpenKfId())
+				.select(CorpKfAccountFollow::getDutyWeek, CorpKfAccountFollow::getStartTime, CorpKfAccountFollow::getEndTime));
+		return GatewayResponse.SUCCESS.newBuilder().toResult(corpKfAccountFollows);
+	}
+}

+ 30 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/corp/wxkf/CorpKfUserController.java

@@ -1,6 +1,8 @@
 package com.cyksj.web.controller.manage.corp.wxkf;
 
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
@@ -8,11 +10,19 @@ import com.cyksj.mapper.corp.kf.CorpKfUserMapper;
 import com.cyksj.model.entity.CorpKfUser;
 import com.cyksj.model.kf.CorpKfUserInfo;
 import com.cyksj.model.request.CorpKfUserMarkReq;
+import com.cyksj.model.views.CustomerConsultationRecordView;
 import com.cyksj.service.corp.WxCorpOps;
+import com.cyksj.service.user.UserBindRelationService;
+import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.param.Operator;
+import com.ejlchina.searcher.util.MapUtils;
 import lombok.RequiredArgsConstructor;
 import org.springframework.dao.DuplicateKeyException;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
 /**
  * admin/企业微信/微信客服
  * 项目名: yhlxj2
@@ -28,6 +38,12 @@ public class CorpKfUserController {
 
 	private final WxCorpOps wxCorpOps;
 
+	private final UserBindRelationService userBindRelationService;
+
+	private final BeanSearcher beanSearcher;
+
+	private final HttpServletRequest request;
+
 	/**
 	 * 微信客户详情
 	 */
@@ -66,6 +82,20 @@ public class CorpKfUserController {
 		return GatewayResponse.SUCCESS.newBuilder().toResult();
 	}
 
+	/**
+	 * 用户当日咨询问题列表
+	 */
+	@GetMapping("/get/consultation/questions")
+	public Result<List<CustomerConsultationRecordView>> getUserConsultationQuestions(Long userId) throws Exception {
+		List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
+		List<CustomerConsultationRecordView> customerConsultationRecords = beanSearcher.searchAll(CustomerConsultationRecordView.class, MapUtils.flatBuilder(request.getParameterMap())
+				.field(CustomerConsultationRecordView::getUserId, userIdList).op(Operator.InList)
+				.field(CustomerConsultationRecordView::getCreatedTime, DateUtil.beginOfDay(DateTime.now()), DateUtil.endOfDay(DateTime.now())).op(Operator.Between)
+				.orderBy(CustomerConsultationRecordView::getId).desc()
+				.build());
+		return GatewayResponse.SUCCESS.newBuilder().toResult(customerConsultationRecords);
+	}
+
 	public CorpKfUser getCorpUser(String externalUserid) throws Exception {
 		CorpKfUser corpKfUser = corpKfUserMapper.selectOne(Wrappers.lambdaQuery(CorpKfUser.class)
 				.eq(CorpKfUser::getExternalUserid, externalUserid)

+ 34 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/goods/GoodsDonQuestionController.java

@@ -1,16 +1,20 @@
 package com.cyksj.web.controller.manage.goods;
 
+import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
 import com.cyksj.mapper.GoodsDonQaRelateMapper;
 import com.cyksj.model.entity.GoodsDonQa;
 import com.cyksj.model.entity.GoodsDonQaRelate;
+import com.cyksj.model.views.CustomerConsultationRecordView;
+import com.cyksj.model.views.CustomerConsultationStatisticsDataView;
 import com.cyksj.model.views.GoodsDonQaView;
 import com.cyksj.service.mange.GoodsDonQaService;
 import com.cyksj.service.mange.RefreshCacheService;
 import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.SearchResult;
+import com.ejlchina.searcher.util.MapBuilder;
 import com.ejlchina.searcher.util.MapUtils;
 import lombok.RequiredArgsConstructor;
 import org.springframework.web.bind.annotation.*;
@@ -142,4 +146,34 @@ public class GoodsDonQuestionController {
 		}
 		return GatewayResponse.SUCCESS.newBuilder().toResult();
 	}
+
+
+	/**
+	 * 客户咨询问题记录列表
+	 */
+	@GetMapping("/get/consultation/record")
+	public Result<SearchResult<CustomerConsultationRecordView>> getConsultationRecord() {
+		SearchResult<CustomerConsultationRecordView> search = beanSearcher.search(CustomerConsultationRecordView.class, MapUtils.flatBuilder(request.getParameterMap())
+				.orderBy(CustomerConsultationRecordView::getUpdateTime).desc()
+				.build());
+		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+	}
+
+
+	/**
+	 * 客户咨询问题记录列表 数据统计
+	 */
+	@GetMapping("/get/consultation/statistics/data")
+	public Result<List<CustomerConsultationStatisticsDataView>> getConsultationStatisticsData(String startTime, String endTime) {
+		String timeSql = StrUtil.EMPTY;
+		if (startTime != null && endTime != null) {
+			timeSql = String.format(" ccr.created_time >= '%s' and ccr.created_time < '%s'", startTime, endTime);
+		}
+		MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap());
+		if (StrUtil.isNotBlank(timeSql)) {
+			builder.put("time", timeSql);
+		}
+		List<CustomerConsultationStatisticsDataView> customerConsultationStatisticsDataViews = beanSearcher.searchAll(CustomerConsultationStatisticsDataView.class, builder.build());
+		return GatewayResponse.SUCCESS.newBuilder().toResult(customerConsultationStatisticsDataViews);
+	}
 }

+ 101 - 0
netflix-web/src/main/java/com/cyksj/web/controller/qa/CorpKfQaController.java

@@ -0,0 +1,101 @@
+package com.cyksj.web.controller.qa;
+
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.mapper.CustomerConsultationRecordMapper;
+import com.cyksj.model.entity.CustomerConsultationRecord;
+import com.cyksj.model.views.GoodsQaShowView;
+import com.cyksj.web.util.StpUserUtil;
+import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.util.MapUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * server/微信客服/咨询问题
+ * 项目名: yhlxj2
+ * 文件名: CorpKfQaController
+ * 创建者: JavaZou
+ * 创建时间:2025/2/7 12:02
+ */
+@RestController
+@RequestMapping("/applet/qa")
+@RequiredArgsConstructor
+public class CorpKfQaController {
+	private final BeanSearcher beanSearcher;
+
+	private final HttpServletRequest request;
+
+	private final CustomerConsultationRecordMapper customerConsultationRecordMapper;
+
+	/**
+	 * 咨询问题级联
+	 */
+	@GetMapping("/get/cascade")
+	public Result<List<GoodsQaShowView>> getCascade() {
+		List<GoodsQaShowView> goodsQaShowViews = beanSearcher.searchAll(GoodsQaShowView.class, MapUtils.builder().build());
+		// 按type分组
+		Map<Integer, List<GoodsQaShowView>> byType = goodsQaShowViews.stream()
+				.collect(Collectors.groupingBy(GoodsQaShowView::getType));
+		List<GoodsQaShowView> result = new ArrayList<>();
+		byType.forEach((type, typeViews) -> {
+			GoodsQaShowView typeGroup = new GoodsQaShowView();
+			typeGroup.setLabel(type == 1 ? "售前" : "售后");
+			typeGroup.setValue(type);
+
+			// 按categoryName分组并转换为QaGoodsType
+			Map<String, List<GoodsQaShowView>> byCategory = typeViews.stream()
+					.collect(Collectors.groupingBy(GoodsQaShowView::getCategoryName));
+
+			List<GoodsQaShowView.QaGoodsType> qaGoodsTypes = new ArrayList<>();
+			byCategory.forEach((categoryName, categoryViews) -> {
+				GoodsQaShowView.QaGoodsType qaType = new GoodsQaShowView.QaGoodsType();
+				qaType.setLabel(categoryName);
+				qaType.setValue(categoryName);
+				// 假设需要将GoodsQaShowView转换为GoodsDon,此处需要具体逻辑
+				Set<Long> dupGids = new HashSet<>();
+				List<GoodsQaShowView.QaGoodsType.GoodsDonQaView> goodsDons = categoryViews.stream()
+						.filter(v -> filterDupGid(v, dupGids))
+						.map(view -> convertToGoodsDon(view)) // 自定义转换方法
+						.collect(Collectors.toList());
+				qaType.setChildren(goodsDons);
+				qaGoodsTypes.add(qaType);
+			});
+			typeGroup.setChildren(qaGoodsTypes);
+			result.add(typeGroup);
+		});
+		return GatewayResponse.SUCCESS.newBuilder().toResult(result);
+	}
+
+
+	/**
+	 * 咨询问题记录
+	 */
+	@PostMapping("/consultation/question/record")
+	public Result<String> recordConsultationQuestion(@RequestBody @Validated CustomerConsultationRecord customerConsultationRecord) {
+		long userId = StpUserUtil.getLoginIdAsLong();
+		customerConsultationRecord.setUserId(userId);
+		customerConsultationRecordMapper.insert(customerConsultationRecord);
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+	private GoodsQaShowView.QaGoodsType.GoodsDonQaView convertToGoodsDon(GoodsQaShowView view) {
+		GoodsQaShowView.QaGoodsType.GoodsDonQaView goodsDonQaView = new GoodsQaShowView.QaGoodsType.GoodsDonQaView();
+		goodsDonQaView.setLabel(view.getTitle());
+		goodsDonQaView.setValue(view.getGoodsId());
+		return goodsDonQaView;
+	}
+
+	private boolean filterDupGid(GoodsQaShowView v, Set<Long> dupGids) {
+		if (dupGids.contains(v.getGoodsId())) {
+			return false;
+		}
+		dupGids.add(v.getGoodsId());
+		return true;
+	}
+}