提交 e02dd06f authored 作者: 窦馨雨's avatar 窦馨雨

合并分支 'qa' 到 'master'

CP促销小程序改造 查看合并请求 !79
package com.wangxiaolu.promotion.controller.activity.temporary;
import com.wangxiaolu.promotion.controller.user.tem.TemporaryInfoQueryController;
import com.wangxiaolu.promotion.domain.user.dao.TemporaryInfoDao;
import com.wangxiaolu.promotion.enums.activity.TemActApproveStatus;
import com.wangxiaolu.promotion.exception.DataException;
......@@ -14,6 +15,7 @@ import com.wangxiaolu.promotion.service.activity.temporary.TemporaryActivityCloc
import com.wangxiaolu.promotion.service.activity.temporary.TemporaryActivityClockQueryService;
import com.wangxiaolu.promotion.service.activity.temporary.TemporaryActivityCoreService;
import com.wangxiaolu.promotion.service.activity.temporary.TemporaryActivityQueryService;
import com.wangxiaolu.promotion.service.wechat.WeChatUserQueryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -41,6 +43,9 @@ public class TemporaryActivityCoreController {
@Autowired
private TemporaryActivityClockCoreService tempActivityClockCoreService;
@Autowired
private WeChatUserQueryService weChatUserQueryService;
/**
* 促销员[今日活动]数据保存
* 返回活动生成id
......@@ -54,6 +59,11 @@ public class TemporaryActivityCoreController {
TemporaryActivityReportedDto temActDto = new TemporaryActivityReportedDto();
BeanUtils.copyProperties(activityVo, temActDto);
temActDto.setId(activityVo.getActivityReportedId());
//保存促销员电话号码
String temporaryPhone = weChatUserQueryService.findUserPhoneByID(activityVo.getTemporaryId());
temActDto.setTemporaryPhone(temporaryPhone);
/**
* 已有id,修改任务
*/
......@@ -78,7 +88,8 @@ public class TemporaryActivityCoreController {
.setActivityPatternId(clockDto.getActivityPatternId())
.setActivityPattern(clockDto.getActivityPattern())
.setProvince(clockDto.getClockProvince())
.setCity(clockDto.getClockCity());
.setCity(clockDto.getClockCity()).setTemporaryPhone(clockDto.getPhone())
;
long reportedId = tempActivityCoreService.activityDataReportedSave(temActDto);
......@@ -128,26 +139,23 @@ public class TemporaryActivityCoreController {
@PutMapping("/reported/approve/submit/{id}")
public R activityReportedSubmit(@PathVariable("id") Long id) {
TemporaryActivityReportedDto activityReportedDto = temporaryActivityQueryService.findTemporaryActivityById(id);
Boolean posFlag = CollectionUtils.isEmpty(activityReportedDto.getPosTaskClockPhotoUrls())|| activityReportedDto.getPosTaskClockPhotoUrls().size() < 1;
if (activityReportedDto.getApproveStatus().equals(TemActApproveStatus.APPROVED) ){
if (posFlag) {
return R.fail(RCode.NOT_POS_TASK_PHOTO_ERROR);
}
return R.success();
}else{
if (CollectionUtils.isEmpty(activityReportedDto.getTgscPhotoInfos()) || activityReportedDto.getTgscPhotoInfos().size() < 2){
if (CollectionUtils.isEmpty(activityReportedDto.getTgscPhotoInfos()) ){
throw new DataException(RCode.NOT_TGSH_PHOTO_ERROR);
}else if (CollectionUtils.isEmpty(activityReportedDto.getTghdPhotoInfos()) || activityReportedDto.getTghdPhotoInfos().size() < 2){
}else if (CollectionUtils.isEmpty(activityReportedDto.getTghdPhotoInfos()) ){
throw new DataException(RCode.NOT_TGHD_PHOTO_ERROR);
}else if (CollectionUtils.isEmpty(activityReportedDto.getTgcjPhotoInfos()) || activityReportedDto.getTgcjPhotoInfos().size() < 2){
}else if (CollectionUtils.isEmpty(activityReportedDto.getTgcjPhotoInfos()) ){
throw new DataException(RCode.NOT_TGCJ_PHOTO_ERROR);
}else if (CollectionUtils.isEmpty(activityReportedDto.getScpPhotoInfos()) ){
throw new DataException(RCode.NOT_SCP_PHOTO_ERROR);
} else if (CollectionUtils.isEmpty(activityReportedDto.getPfzpPhotoInfos()) ){
throw new DataException(RCode.NOT_PFZP_PHOTO_ERROR);
} else if (CollectionUtils.isEmpty(activityReportedDto.getGfqPhotoInfos()) ){
throw new DataException(RCode.NOT_GFQ_PHOTO_ERROR);
} else if (CollectionUtils.isEmpty(activityReportedDto.getScbbwlPhotoInfos()) ){
throw new DataException(RCode.NOT_SCBBWL_PHOTO_ERROR);
}
tempActivityCoreService.activityReportedSubmit(id);
if (posFlag) {
return R.fail(RCode.NOT_POS_TASK_PHOTO_ERROR);
}
}
return R.success();
}
......
......@@ -17,6 +17,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.io.File;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.YearMonth;
......
package com.wangxiaolu.promotion.controller.user.location;
import com.wangxiaolu.promotion.result.basedata.R;
import com.wangxiaolu.promotion.service.user.TencentLocationQueryService;
import com.wangxiaolu.promotion.utils.TencentMapUtil;
import org.springframework.beans.factory.annotation.Autowired;
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;
/**
* @Author: DouXinYu
* @Date: 2026-01-12 11:10
* @Description:
*/
@RestController
@RequestMapping("/user/location/query")
public class TencentLocationController {
@Autowired
TencentLocationQueryService tencentLocationQueryService;
@GetMapping("/reverse")
public R getDefaultLocation(@RequestParam("lng") double lng, @RequestParam("lat") double lat){
TencentMapUtil.AddressInfo addressByLngLat = tencentLocationQueryService.getAddressByLngLat(lng, lat);
return R.success(addressByLngLat);
}
// @GetMapping("/list")
// public R getProvinceDistinctList(){
// return R.success(tencentLocationQueryService.getAddressList());
// }
}
package com.wangxiaolu.promotion.controller.user.tem;
import com.wangxiaolu.promotion.common.redis.RedisKeys;
import com.wangxiaolu.promotion.common.redis.service.RedisCache;
import com.wangxiaolu.promotion.common.util.DataUtils;
import com.wangxiaolu.promotion.common.util.EnvUtil;
import com.wangxiaolu.promotion.domain.user.mapper.entity.WxTemporaryInfoDelayDO;
import com.wangxiaolu.promotion.exception.ParamException;
import com.wangxiaolu.promotion.pojo.activity.temporary.dto.WxTemporaryInfoDelayDtO;
import com.wangxiaolu.promotion.pojo.user.dto.WxTemporaryInfoDto;
import com.wangxiaolu.promotion.pojo.user.vo.WxTemporaryEnrollVo;
import com.wangxiaolu.promotion.result.basedata.R;
import com.wangxiaolu.promotion.result.basedata.RCode;
import com.wangxiaolu.promotion.service.wechat.WeChatUserCoreService;
import com.wangxiaolu.promotion.service.wechat.WeChatUserQueryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
/**
* @author : liqiulin
......@@ -33,6 +36,8 @@ public class TemporaryInfoCoreController {
@Autowired
private WeChatUserCoreService weChatUserCoreService;
@Autowired
private WeChatUserQueryService weChatUserQueryService;
@Autowired
private RedisCache redisCache;
@Autowired
private EnvUtil envUtil;
......@@ -58,7 +63,8 @@ public class TemporaryInfoCoreController {
// if (envUtil.isLive()) {
// String redisKey = RedisKeys.UserKeys.PHONE_VER_CODE.getKey() + wxTemporaryEnrollVo.getPhone();
// String phoneCodeOld = redisCache.get(redisKey);
// if (StringUtils.isBlank(phoneCodeOld) || !phoneCodeOld.equals(wxTemporaryEnrollVo.getPhoneCode())) {
// if (StringUtils.isBlan
// k(phoneCodeOld) || !phoneCodeOld.equals(wxTemporaryEnrollVo.getPhoneCode())) {
// throw new ParamException(RCode.TENCENT_SMS_PHONE_CODE_ERROR, null);
// }
// redisCache.removeKey(redisKey);
......@@ -69,4 +75,30 @@ public class TemporaryInfoCoreController {
return R.success(weChatUserCoreService.saveWxUserInfoTemporary(temporaryDto));
}
@PutMapping("/temporary/changeStoreInfo")
public R changeUserInfo(@RequestBody WxTemporaryEnrollVo wxTemporaryEnrollVo) {
if (wxTemporaryEnrollVo.getOpenId() == null) {
throw new ParamException(RCode.WX_OPENID_PARAM_ERROR, null);
}
Map<String, Object> temporaryWorkStore = weChatUserQueryService.findTemporaryWorkStore(wxTemporaryEnrollVo.getOpenId(), LocalDate.now().toString());
if (Objects.isNull(temporaryWorkStore) ) {
WxTemporaryInfoDto temporaryDto = new WxTemporaryInfoDto();
BeanUtils.copyProperties(wxTemporaryEnrollVo, temporaryDto);
weChatUserCoreService.saveWxUserStoreInfoTemporary(temporaryDto);
return R.success(true);
}
// 转换为延迟表实体
WxTemporaryInfoDelayDtO delayEntity = new WxTemporaryInfoDelayDtO();
BeanUtils.copyProperties(wxTemporaryEnrollVo, delayEntity);
// 设置数据日期(当天)、创建时间、未处理状态
delayEntity.setCreateTime(LocalDateTime.now());
delayEntity.setIsProcessed(0);
// 保存到延迟表
boolean saveResult = weChatUserCoreService.saveDelayData(delayEntity);
return R.success(saveResult);
}
}
......@@ -51,7 +51,7 @@ public class TemporaryInfoQueryController {
if (!DataUtils.phonePattern(wxTemporaryLoginVo.getPhone())) {
throw new ParamException(RCode.PHONE_PARAM_ERROR, null);
}
LoginVo loginVo = weChatUserQueryService.temporaryLoginByPhone(wxTemporaryLoginVo.getPhone());
LoginVo loginVo = weChatUserQueryService.loginTemporaryByOpenIdAndPhone(wxTemporaryLoginVo.getOpenId(),wxTemporaryLoginVo.getPhone());
return R.success(loginVo);
}
......@@ -72,12 +72,20 @@ public class TemporaryInfoQueryController {
/**
* 注册时获取门店列表
* @param temporaryRegisterVo 临时注册信息
* @param
* @return 门店列表
*/
@PostMapping("/temporary/findStoreList")
public R findStoreList(@RequestBody TemporaryRegisterVo temporaryRegisterVo) {
return R.success(weChatUserQueryService.findStoreList(temporaryRegisterVo));
@GetMapping("/temporary/findStoreList")
public R findStoreList(@RequestParam("planDay") String planDay,@RequestParam(value = "storeNameKeyword",required = false) String storeNameKeyword) {
return R.success(weChatUserQueryService.findStoreList(planDay,storeNameKeyword));
}
@PostMapping("/temporary/findTemporaryWorkStore")
public R findTemporaryInfo(@RequestBody TemporaryRegisterVo temporaryRegisterVo) {
if (StringUtils.isBlank(temporaryRegisterVo.getOpenId())) {
throw new ParamException(RCode.LOGIN_PARAM_ERROR, null);
}
return R.success(weChatUserQueryService.findTemporaryWorkStore(temporaryRegisterVo.getOpenId(),temporaryRegisterVo.getDate()));
}
private void phontAndOpenIdVerify(WxTemporaryLoginVo wxTemporaryLoginVo) {
......
......@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
......@@ -140,6 +141,28 @@ public class TemporaryActivityReportedDO implements Serializable {
*/
private Integer isDelete;
// 促销员联系电话
private String temporaryPhone;
// 执行方式
private String executeModePlan;
// 配发试吃品是否有黑鸭口味
private String hasBlackDuckFlavor;
// 配发黑鸭试吃品(根)
private Integer blackDuckTastingNum;
// 配发卤香试吃品(根)
private Integer braisedTastingNum;
// 配发赠品个数
private Integer giftNum;
// 推广活动销额(元)
private BigDecimal promotionSales;
// 黑鸭销售(元)
private BigDecimal blackDuckSales;
// 礼盒销售(元)
private BigDecimal giftBoxSales;
// 手工POS机销额
private String posManualSalesRecord;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
......@@ -42,9 +42,9 @@ public interface ActivityPlanInfoDao {
/**
* 根据位置信息查询当前月份的促销门店
* @param temporaryRegisterVo 查询条件参数
* @param planDay 查询条件参数
* @return 门店信息列表
*/
List<Map<String, String>> findThisMonthStoreListByLocation(TemporaryRegisterVo temporaryRegisterVo);
List<Map<String, String>> findThisMonthStoreListByLocation(String planDay,String storeNameKeyword);
}
......@@ -129,28 +129,14 @@ public class ActivityPlanInfoDaoImpl implements ActivityPlanInfoDao {
/**
* 根据地理位置信息查询本店本月门店列表
* @param temporaryRegisterVo 查询条件参数
* @param planDay 查询条件参数
* @return 门店列表
*/
@Override
public List<Map<String, String>> findThisMonthStoreListByLocation(TemporaryRegisterVo temporaryRegisterVo) {
LambdaQueryWrapper<ActivityPlanInfoDo> qw = new LambdaQueryWrapper<>();
qw.likeRight(ActivityPlanInfoDo::getDate,temporaryRegisterVo.getMonth());
if (StringUtils.isNotBlank(temporaryRegisterVo.getProvince())){
qw.eq(ActivityPlanInfoDo::getProvince,temporaryRegisterVo.getProvince());
}
if (StringUtils.isNotBlank(temporaryRegisterVo.getCity())){
qw.eq(ActivityPlanInfoDo::getCity,temporaryRegisterVo.getCity());
}
List<ActivityPlanInfoDo> activityPlanInfoDoList = activityPlanInfoMapper.selectList(qw);
List<Map<String, String>> resultList = activityPlanInfoDoList.stream().map(activityPlanInfoDo -> {
Map<String, String> map = new HashMap<>();
map.put("storeCode", activityPlanInfoDo.getStoreCode());
map.put("storeName", activityPlanInfoDo.getStoreName());
return map;
}).collect(Collectors.toList());
return resultList;
public List<Map<String, String>> findThisMonthStoreListByLocation(String planDay,String storeNameKeyword) {
List<Map<String, String>> activityPlanInfoDoList = activityPlanInfoMapper.selectStoreInfoByCondition(planDay,storeNameKeyword);
return activityPlanInfoDoList;
}
private LambdaQueryWrapper<ActivityPlanInfoDo> buildWrapper(ActivityPlanInfoWrapper wrapper) {
......
......@@ -7,10 +7,12 @@ import com.wangxiaolu.promotion.domain.manage.wrapperQo.ActivityPlanInfoWrapper;
import com.wangxiaolu.promotion.pojo.activity.planv2.dto.ActivityPlanInfoDto;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author a02200059
......@@ -39,6 +41,28 @@ public interface ActivityPlanInfoMapper extends BaseMapper<ActivityPlanInfoDo> {
List<ActivityPlanInfoDo> findClockList(ActivityPlanInfoWrapper wrapper);
void updateByIds(@Param("ids") List<Long> planIds, @Param("planDto") ActivityPlanInfoDto planDto);
/**
* 查询符合条件的门店编码和名称(去重)
* @param date 日期阈值(格式:yyyy-MM-dd)
* @param storeNameKeyword 门店名称关键词
* @return 包含store_code和store_name的结果列表
*/
@Select({
"<script>",
"SELECT DISTINCT api.store_code AS storeCode, api.store_name AS storeName ",
"FROM activity_plan_info api ",
"WHERE api.`date` >= #{date} ",
"<if test='storeNameKeyword != null and storeNameKeyword != \"\"'>",
"AND api.store_name LIKE CONCAT('%', #{storeNameKeyword}, '%')",
"</if>",
"AND is_delete = 1 ",
"</script>"
})
List<Map<String, String>> selectStoreInfoByCondition(
@Param("date") String date,
@Param("storeNameKeyword") String storeNameKeyword
);
}
......
......@@ -161,14 +161,23 @@ public class ActivityPlanInfoDo implements Serializable {
// private BigDecimal incidentals;
/**
* 是否有试吃台
* 门店是否已配试吃台
*/
private String temWlSct;
/**
* 活动品项:老品/黑鸭/散称
* 活动品项:老品/黑鸭/散称/礼盒
*/
private String prdClass;
/**
* 活动执行方式:经销商/第三方
*/
private String planExecuteMode;
/**
* 门店是否允许使用试吃台
*/
private String isCanUseTasteCounter;
/**
* 是否执行:1:执行;0:未执行;
*/
......
......@@ -4,6 +4,9 @@ import com.wangxiaolu.promotion.domain.user.wrapperQo.TemporaryWrapper;
import com.wangxiaolu.promotion.pojo.PageInfo;
import com.wangxiaolu.promotion.pojo.user.dto.WxTemporaryInfoDto;
import java.util.List;
import java.util.Map;
/**
* @author : liqiulin
* @date : 2024-04-08 16
......@@ -28,4 +31,11 @@ public interface TemporaryInfoDao {
void updateById(WxTemporaryInfoDto temporaryDto);
WxTemporaryInfoDto selectByPhone(String phone);
int saveWxUserStoreInfoTemporary(WxTemporaryInfoDto temporaryDto);
Map<String,Object> selectStoreAndPlan(String openId, String date);
int batchInsertOrUpdate(List<WxTemporaryInfoDto> list);
}
package com.wangxiaolu.promotion.domain.user.dao;
import com.wangxiaolu.promotion.domain.user.mapper.entity.WxTemporaryInfoDelayDO;
import com.wangxiaolu.promotion.pojo.activity.temporary.dto.WxTemporaryInfoDelayDtO;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
@Repository
public interface WxTemporaryInfoDelayDao {
// 查询指定时间范围未处理的数据
List<WxTemporaryInfoDelayDtO> selectUnprocessedData(LocalDateTime startTime, LocalDateTime endTime);
// 插入单条延迟数据
int insert(WxTemporaryInfoDelayDtO delayEntity);
// 批量更新处理状态
int batchUpdateProcessedStatus(List<Long> idList, Integer isProcessed);
}
\ No newline at end of file
......@@ -21,6 +21,7 @@ import org.springframework.util.CollectionUtils;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
......@@ -94,6 +95,53 @@ public class TemporaryInfoDaoImpl implements TemporaryInfoDao {
return transitionDto(temDo);
}
@Override
public int saveWxUserStoreInfoTemporary(WxTemporaryInfoDto temporaryDto) {
TemporaryInfoDO entity = new TemporaryInfoDO();
BeanUtils.copyProperties(temporaryDto, entity);
return temporaryInfoMapper.updateById(entity);
}
@Override
public Map<String,Object> selectStoreAndPlan(String openId, String date) {
return temporaryInfoMapper.selectStoreAndPlan(openId,date);
}
@Override
public int batchInsertOrUpdate(List<WxTemporaryInfoDto> list) {
// 空列表防护:避免无效SQL执行
int affectRows;
if (CollectionUtils.isEmpty(list)) {
return 0;
}
try {
// 1. 批量转换DTO → DO
List<TemporaryInfoDO> doList = new ArrayList<>(list.size());
for (WxTemporaryInfoDto dto : list) {
TemporaryInfoDO entity = new TemporaryInfoDO();
BeanUtils.copyProperties(dto, entity);
doList.add(entity);
}
// 2. 调用Mapper层的批量插入/更新方法
affectRows = temporaryInfoMapper.batchInsertOrUpdate(doList);
// 3. 异常捕获:处理唯一键冲突(复用项目已有异常逻辑)
} catch (Exception e) {
affectRows = 0;
String eMsg = e.getCause() != null ? e.getCause().getMessage() : e.getMessage();
if (eMsg.contains("for key 'open_id_unique'")) {
throw new ParamException(RCode.USER_WXOPENID_UNIQUE_ERROR);
} else if (eMsg.contains("for key 'phone_unique'")) {
throw new ParamException(RCode.USER_PHONE_UNIQUE_ERROR);
}
}
return affectRows;
}
private LambdaQueryWrapper<TemporaryInfoDO> buildQueryList(TemporaryWrapper tw) {
LambdaQueryWrapper<TemporaryInfoDO> queryWrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(tw.getOpenId())) {
......
package com.wangxiaolu.promotion.domain.user.dao.impl;
import com.wangxiaolu.promotion.common.util.BeanUtils;
import com.wangxiaolu.promotion.domain.user.dao.WxTemporaryInfoDelayDao;
import com.wangxiaolu.promotion.domain.user.mapper.WxTemporaryInfoDelayMapper;
import com.wangxiaolu.promotion.domain.user.mapper.entity.WxTemporaryInfoDelayDO;
import com.wangxiaolu.promotion.pojo.activity.temporary.dto.WxTemporaryInfoDelayDtO;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class WxTemporaryInfoDelayDaoImpl implements WxTemporaryInfoDelayDao {
@Resource
private WxTemporaryInfoDelayMapper delayMapper;
@Override
public List<WxTemporaryInfoDelayDtO> selectUnprocessedData(LocalDateTime startTime, LocalDateTime endTime) {
// 1. 查询数据库获取DO列表
List<WxTemporaryInfoDelayDO> wxTemporaryInfoDelayDOS = delayMapper.selectByTimeAndStatus(startTime, endTime, 0);
// 2. 调用类内的通用转换方法完成DO转DTO
return copyListProperties(wxTemporaryInfoDelayDOS, WxTemporaryInfoDelayDtO.class);
}
@Override
public int insert(WxTemporaryInfoDelayDtO delayEntity) {
WxTemporaryInfoDelayDO delayDO = new WxTemporaryInfoDelayDO();
BeanUtils.copyProperties(delayEntity, delayDO);
return delayMapper.insert(delayDO);
}
@Override
public int batchUpdateProcessedStatus(List<Long> idList, Integer isProcessed) {
if (idList == null || idList.isEmpty()) {
return 0;
}
return delayMapper.batchUpdateStatus(idList, isProcessed);
}
private <S, T> List<T> copyListProperties(List<S> sourceList, Class<T> targetClass) {
// 空值判断,避免空指针
if (sourceList == null || sourceList.isEmpty()) {
return new ArrayList<>();
}
// 批量转换源列表为目标列表
return sourceList.stream()
.map(source -> {
try {
// 反射创建目标对象实例
T target = targetClass.getDeclaredConstructor().newInstance();
// 拷贝属性
BeanUtils.copyProperties(source, target);
return target;
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException |
InvocationTargetException e) {
// 转换失败抛出运行时异常,便于排查问题
throw new RuntimeException("对象列表转换失败,目标类:" + targetClass.getName(), e);
}
})
.collect(Collectors.toList());
}
}
\ No newline at end of file
......@@ -4,8 +4,12 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wangxiaolu.promotion.domain.user.mapper.entity.TemporaryInfoDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Map;
/**
* @author a02200059
* @description 针对表【temporary_info】的数据库操作Mapper
......@@ -19,6 +23,22 @@ public interface TemporaryInfoMapper extends BaseMapper<TemporaryInfoDO> {
// 根据openId查询非保密数据,保密数据不返回:身份证号、身份证照片等信息
TemporaryInfoDO getUnimportantData(@Param("openId") String openId);
@Select({
"SELECT ti.store_code AS storeCode, ",
" ti.store_name AS storeName, ",
" api.id AS planId ",
"FROM temporary_info ti ",
"LEFT JOIN activity_plan_info api ON ti.store_name = api.store_name ",
" AND api.is_delete = 1 ",
" AND api.`date` = #{date} ",
"WHERE ti.open_id = #{openId}"
})
Map<String, Object> selectStoreAndPlan(
@Param("openId") String openId,
@Param("date") String date
);
int batchInsertOrUpdate(@Param("list") List<TemporaryInfoDO> doList);
}
......
package com.wangxiaolu.promotion.domain.user.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wangxiaolu.promotion.domain.user.mapper.entity.WxTemporaryInfoDelayDO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
/**
* 延迟临时表Mapper
*/
@Repository
@Mapper
public interface WxTemporaryInfoDelayMapper extends BaseMapper<WxTemporaryInfoDelayDO> {
/**
* 查询指定时间范围内未处理的数据
*/
List<WxTemporaryInfoDelayDO> selectByTimeAndStatus(
@Param("startTime") LocalDateTime startTime,
@Param("endTime") LocalDateTime endTime,
@Param("isProcessed") Integer isProcessed
);
/**
* 批量更新处理状态
*/
int batchUpdateStatus(
@Param("idList") List<Long> idList,
@Param("isProcessed") Integer isProcessed
);
}
\ No newline at end of file
package com.wangxiaolu.promotion.domain.user.mapper.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 微信门店数据延迟保存临时表实体
*/
@Data
@TableName("wx_temporary_info_delay")
public class WxTemporaryInfoDelayDO {
/**
* 主键ID
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 微信OpenId
*/
private String openId;
/**
* 门店名称
*/
private String storeName;
/**
* 门店编码
*/
private String storeCode;
/**
* 联系电话
*/
private String contactPhone;
/**
* 数据暂存时间
*/
private LocalDateTime createTime;
/**
* 是否已处理:0-未处理 1-已处理
*/
private Integer isProcessed;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}
\ No newline at end of file
......@@ -39,20 +39,29 @@ public enum ActivityPhotoType {
* 下班打卡图片
*/
CLOCK_OUT(7),
/**
* 试吃照片
*/
TASTY_PHOTO(8),
/**
* 当日销量POS机页面凭证
*/
POS_SELL_VOUCHER(8),
POS_SELL_VOUCHER(9),
/**
* POS照片
* 高峰期在岗
* 更改固定任务:下午18点15任务
*/
POS_PHOTO(9),
RANDOM_TASK(10),
/**
* 随机任务照片
* 更改固定任务:下午18点15任务
* 配发赠品照片
*/
SEND_GIFT_PHOTO(11),
/**
* 试吃素材
*/
RANDOM_TASK(10)
TASTY_MATERIALS(12)
;
private int type;
......
......@@ -77,4 +77,5 @@ public class ActivityPlanInfoVo {
private String lineName;
}
......@@ -158,14 +158,24 @@ public class ActivityPlanInfoDto implements Serializable {
*/
// private BigDecimal incidentals;
/**
* 是否有试吃台
* 门店是否已配试吃台
*/
private String temWlSct;
/**
* 活动品项:老品/黑鸭/散称
* 活动品项:老品/黑鸭/散称/礼盒
*/
private String prdClass;
/**
* 活动执行方式:经销商/第三方
*/
private String planExecuteMode;
/**
* 门店是否允许使用试吃台
*/
private String isCanUseTasteCounter;
/**
* 是否执行:1:执行;0:未执行;
*/
......
......@@ -80,12 +80,21 @@ public class ActivityPlanOperVo {
private LocalDateTime noonClockInTime;
/**
* 是否有试吃台
* 门店是否可用试吃台
*/
private String isCanUseTasteCounter;
/**
* 门店是否配有试吃台
*/
private String temWlSct;
/**
* 活动品项:老品/黑鸭/散称
* 活动品项:老品/黑鸭/散称/礼盒
*/
private String prdClass;
/**
* 执行方式
*/
private String planExecuteMode;
private List<Long> planIds;
}
......@@ -156,6 +156,31 @@ public class TemporaryActivityReportedDto {
private List<TemporaryActivityPhotoDto> randTaskClockPhotoUrls;
private List<TemporaryActivityPhotoDto> posTaskClockPhotoUrls;
/**
* 试吃品照片
*/
private List<String> scpPhotoUrls;
private List<TemporaryActivityPhotoDto>scpPhotoInfos;
private List<String> scpChangePhotoUrls;
/**
* 配发赠品照片
*/
private List<String> pfzpPhotoUrls;
private List<TemporaryActivityPhotoDto>pfzpPhotoInfos;
private List<String> pfzpChangePhotoUrls;
/**
* 高峰期在岗
*/
private List<String> gfqPhotoUrls;
private List<TemporaryActivityPhotoDto> gfqPhotoInfos;
private List<String> gfqChangePhotoUrls;
/**
* 试吃必备物料照片
*/
private List<String> scbbwlPhotoUrls;
private List<TemporaryActivityPhotoDto> scbbwlPhotoInfos;
private List<String> scbbwlChangePhotoUrls;
/**
* 创建时间
......@@ -203,6 +228,8 @@ public class TemporaryActivityReportedDto {
// 礼盒销售(元)
private BigDecimal giftBoxSales;
// 手工POS机销额
private String posManualSalesRecord;
public void approvedDataVerify(){
}
......
package com.wangxiaolu.promotion.pojo.activity.temporary.dto;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 微信门店数据延迟保存临时表实体
*/
@Data
public class WxTemporaryInfoDelayDtO {
/**
* 主键ID
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 微信OpenId
*/
private String openId;
/**
* 门店名称
*/
private String storeName;
/**
* 门店地址
*/
private String storeCode;
/**
* 联系电话
*/
private String contactPhone;
/**
* 数据暂存时间
*/
private LocalDateTime createTime;
/**
* 是否已处理:0-未处理 1-已处理
*/
private Integer isProcessed;
/**
* 更新时间
*/
private LocalDateTime updateTime;
}
\ No newline at end of file
......@@ -67,6 +67,24 @@ public class TemporaryActivityDataVo {
*/
private List<String> psvPhotoUrls;
private List<String> psvChangePhotoUrls;
/**
* 试吃品照片
*/
private List<String> scpPhotoUrls;
/**
* 配发赠品照片
*/
private List<String> pfzpPhotoUrls;
/**
* 高峰期在岗
*/
private List<String> gfqPhotoUrls;
/**
* 试吃必备物料照片
*/
private List<String> scbbwlPhotoUrls;
// 促销员联系电话
private String temporaryPhone;
// 执行方式
......@@ -85,5 +103,7 @@ public class TemporaryActivityDataVo {
private BigDecimal blackDuckSales;
// 礼盒销售(元)
private BigDecimal giftBoxSales;
// POS销额手工统计记录
private String posManualSalesRecord;
}
......@@ -13,23 +13,9 @@ import java.util.Date;
@Data
public class TemporaryRegisterVo {
/**
* 当前月份 YYYY-MM
*/
private YearMonth month;
/**
* 省份
*/
private String province;
/**
* 城市
*/
private String city;
/**
* 区
*/
private String area;
private String openId;
private String phone;
private Long id;
private String date;
}
......@@ -18,6 +18,7 @@ import com.wangxiaolu.promotion.pojo.activity.planv2.dto.ActivityPlanInfoDto;
import com.wangxiaolu.promotion.pojo.activity.temporary.dto.TemporaryActivityMarketCellDto;
import com.wangxiaolu.promotion.pojo.activity.temporary.dto.TemporaryActivityReportedDto;
import com.wangxiaolu.promotion.pojo.activity.temporary.vo.TemporaryActivityMarketCellVo;
import com.wangxiaolu.promotion.pojo.user.dto.WxTemporaryInfoDto;
import com.wangxiaolu.promotion.result.basedata.RCode;
import com.wangxiaolu.promotion.service.activity.temporary.TemporaryActivityClockQueryService;
import com.wangxiaolu.promotion.service.activity.temporary.TemporaryActivityCoreService;
......@@ -88,6 +89,7 @@ public class TemporaryActivityCoreServiceImpl implements TemporaryActivityCoreSe
// 保存图片
saveActivityPhotoV2(temActDto);
// 售卖单元新增保存
// String key = temMarketCellRedisKey(temActDto.getTemporaryId());
// temporaryActivityMarketCellDao.saveList(reportedId,redisCache.getToJsonArray(key));
......@@ -107,12 +109,11 @@ public class TemporaryActivityCoreServiceImpl implements TemporaryActivityCoreSe
// 图片增量保存
saveActivityPhotoV2(temActDto);
// 如果数量小于2 修改状态为 SUBMITTED 已保存
if(ObjectUtil.isAllNotEmpty(temActDto.getTgscPhotoUrls(),temActDto.getTghdPhotoUrls(),temActDto.getTgcjPhotoUrls())
&& temActDto.getTgscPhotoUrls().size()>=2 && temActDto.getTghdPhotoUrls().size()>=2 && temActDto.getTgcjPhotoUrls().size()>=2 ){
if(ObjectUtil.isAllNotEmpty(temActDto.getTgscPhotoUrls(),temActDto.getTghdPhotoUrls(),temActDto.getTgcjPhotoUrls())){
temActDto.setApproveStatus(TemActApproveStatus.APPROVED);
}else {
temActDto.setApproveStatus(TemActApproveStatus.SUBMITTED);
}
WxTemporaryInfoDto wxTemporaryInfoDto = temporaryInfoDao.selectOneById(temActDto.getTemporaryId());
temActDto.setTemporaryPhone(wxTemporaryInfoDto.getPhone());
temporaryActivityReportedDao.updateById(temActDto);
// 售卖单元新增保存
......@@ -283,10 +284,38 @@ public class TemporaryActivityCoreServiceImpl implements TemporaryActivityCoreSe
tempActivityPhotoDao.saveReportedList(null, temporaryId, reportedId, ActivityPhotoType.TGCJ.getType(), temActDto.getTgcjPhotoUrls());
}
// POS机页面凭证
// if (!CollectionUtils.isEmpty(temActDto.getPsvPhotoUrls())) {
// tempActivityPhotoDao.saveReportedList(temporaryId, reportedId, ActivityPhotoType.POS_SELL_VOUCHER.getType(), temActDto.getPsvPhotoUrls());
// }
// 试吃品
if (CollectionUtils.isEmpty(temActDto.getScpPhotoUrls())) {
tempActivityPhotoDao.deleteList(reportedId, ActivityPhotoType.TASTY_PHOTO.getType());
}else {
tempActivityPhotoDao.saveReportedList(null,temporaryId, reportedId, ActivityPhotoType.TASTY_PHOTO.getType(), temActDto.getScpPhotoUrls());
}
// 配发赠品照片
if (CollectionUtils.isEmpty(temActDto.getPfzpPhotoUrls())) {
tempActivityPhotoDao.deleteList(reportedId, ActivityPhotoType.SEND_GIFT_PHOTO.getType());
} else {
tempActivityPhotoDao.saveReportedList(null,temporaryId, reportedId, ActivityPhotoType.SEND_GIFT_PHOTO.getType(), temActDto.getPfzpPhotoUrls());
}
//POS机页面凭证
if (CollectionUtils.isEmpty(temActDto.getPsvPhotoUrls())) {
tempActivityPhotoDao.deleteList(reportedId, ActivityPhotoType.POS_SELL_VOUCHER.getType());
} else {
tempActivityPhotoDao.saveReportedList(null,temporaryId, reportedId, ActivityPhotoType.POS_SELL_VOUCHER.getType(), temActDto.getPsvPhotoUrls());
}
//高峰期在岗
if (CollectionUtils.isEmpty(temActDto.getGfqPhotoUrls())) {
tempActivityPhotoDao.deleteList(reportedId, ActivityPhotoType.RANDOM_TASK.getType());
} else {
tempActivityPhotoDao.saveReportedList(null,temporaryId, reportedId, ActivityPhotoType.RANDOM_TASK.getType(), temActDto.getGfqPhotoUrls());
}
// 试吃必备物料照片
if (CollectionUtils.isEmpty(temActDto.getScbbwlPhotoUrls())) {
tempActivityPhotoDao.deleteList(reportedId, ActivityPhotoType.TASTY_MATERIALS.getType());
} else {
tempActivityPhotoDao.saveReportedList(null,temporaryId, reportedId, ActivityPhotoType.TASTY_MATERIALS.getType(), temActDto.getScbbwlPhotoUrls());
}
}
// private void saveActivityPhoto(TemporaryActivityReportedDto temActDto) {
......
......@@ -80,20 +80,13 @@ public class TemporaryActivityQueryServiceImpl implements TemporaryActivityQuery
wer.setTemporaryId(dto.getTemporaryId())
.setReportedId(activityId);
TemporaryClockDto clockInfo = temporaryActivityClockQueryService.findClockInfo(wer);
// 设置 随机任务照片
// 设置 高峰期在岗
TemporaryPhotoWrapper wrapper = new TemporaryPhotoWrapper();
wrapper.setTemporaryId(dto.getTemporaryId())
.setClockId(clockInfo.getId())
.setType(ActivityPhotoType.RANDOM_TASK.getType() );
List<TemporaryActivityPhotoDto> temporaryActivityPhotoDtos = temporaryActivityPhotoDao.selectPhotos(wrapper);
dto.setRandTaskClockPhotoUrls(temporaryActivityPhotoDtos);
TemporaryPhotoWrapper posWrapper = new TemporaryPhotoWrapper();
posWrapper.setTemporaryId(dto.getTemporaryId())
.setClockId(clockInfo.getId())
.setType(ActivityPhotoType.POS_PHOTO.getType() );
List<TemporaryActivityPhotoDto> posTaskClockPhotoUrls = temporaryActivityPhotoDao.selectPhotos(posWrapper);
dto.setPosTaskClockPhotoUrls(posTaskClockPhotoUrls);
return dto;
}
......@@ -132,7 +125,11 @@ public class TemporaryActivityQueryServiceImpl implements TemporaryActivityQuery
dto.setTgscPhotoUrls(reportedGroup.get(ActivityPhotoType.TGSC.getType()));
dto.setTghdPhotoUrls(reportedGroup.get(ActivityPhotoType.TGHD.getType()));
dto.setTgcjPhotoUrls(reportedGroup.get(ActivityPhotoType.TGCJ.getType()));
dto.setScpPhotoUrls(reportedGroup.get(ActivityPhotoType.TASTY_PHOTO.getType()));
dto.setPsvPhotoUrls(reportedGroup.get(ActivityPhotoType.POS_SELL_VOUCHER.getType()));
dto.setPfzpPhotoUrls(reportedGroup.get(ActivityPhotoType.SEND_GIFT_PHOTO.getType()));
dto.setGfqPhotoUrls(reportedGroup.get(ActivityPhotoType.RANDOM_TASK.getType()));
dto.setScbbwlPhotoUrls(reportedGroup.get(ActivityPhotoType.TASTY_MATERIALS.getType()));
}
}
......@@ -145,7 +142,11 @@ public class TemporaryActivityQueryServiceImpl implements TemporaryActivityQuery
dto.setTgscPhotoInfos(reportedGroup.get(ActivityPhotoType.TGSC.getType()));
dto.setTghdPhotoInfos(reportedGroup.get(ActivityPhotoType.TGHD.getType()));
dto.setTgcjPhotoInfos(reportedGroup.get(ActivityPhotoType.TGCJ.getType()));
dto.setScpPhotoInfos(reportedGroup.get(ActivityPhotoType.TASTY_PHOTO.getType()));
dto.setPsvPhotoInfos(reportedGroup.get(ActivityPhotoType.POS_SELL_VOUCHER.getType()));
dto.setPfzpPhotoInfos(reportedGroup.get(ActivityPhotoType.SEND_GIFT_PHOTO.getType()));
dto.setGfqPhotoInfos(reportedGroup.get(ActivityPhotoType.RANDOM_TASK.getType()));
dto.setScbbwlPhotoInfos(reportedGroup.get(ActivityPhotoType.TASTY_MATERIALS.getType()));
}
}
}
......@@ -73,7 +73,7 @@ public class TemporaryActivityTaskClockServiceImpl extends ServiceImpl<Temporary
// 随机生成 下午5:30~7:30 之间的Date
// LocalTime randomTime = LocalTime.of(17, 30, 0).plusMinutes(ThreadLocalRandom.current().nextInt(0, 120));
// 随机生成改成下午6点15生成
LocalTime randomTime = LocalTime.of(18, 15, 0);
LocalTime randomTime = LocalTime.of(18, 0, 0);
Date randomDate = DateUtils.parseDateByLocalTime(randomTime);
// 生成
......@@ -109,7 +109,7 @@ public class TemporaryActivityTaskClockServiceImpl extends ServiceImpl<Temporary
.setClockId(dto.getId())
.setReportId(dto.getReportedId())
.setCreateDate(DateUtil.today())
.setTaskType(ActivityPhotoType.POS_PHOTO.getType())
.setTaskType(ActivityPhotoType.POS_SELL_VOUCHER.getType())
.setIsDelete(FlagType.NO.getType());
TemporaryActivityTaskClockDO taskClockDO1 = temporaryActivityTaskClockDao.selectOne(wrapper);
if (ObjectUtil.isNotEmpty(taskClockDO1)) {
......@@ -124,7 +124,7 @@ public class TemporaryActivityTaskClockServiceImpl extends ServiceImpl<Temporary
.setReportedId(null)
.setPlanId(dto.getPlanId())
.setClockTime(null)
.setTaskType(ActivityPhotoType.POS_PHOTO.getType())
.setTaskType(ActivityPhotoType.POS_SELL_VOUCHER.getType())
.setTaskStatus(ActivityClockTaskStatus.STARTING.getType())
.setRequiredlockTime(null)
.setActivityPatternId(dto.getActivityPatternId())
......@@ -240,11 +240,11 @@ public class TemporaryActivityTaskClockServiceImpl extends ServiceImpl<Temporary
if (taskClockDO.getTaskType() == ActivityPhotoType.RANDOM_TASK.getType() && requiredLockTime != null) {
Date now = new Date();
if (now.compareTo(requiredLockTime) > 0
&& now.compareTo(DateUtil.offsetMinute(requiredLockTime,15)) < 0) {
&& now.compareTo(DateUtil.offsetMinute(requiredLockTime,30)) < 0) {
res.setEditableFlag(true);
}
}
if (taskClockDO.getTaskType() == ActivityPhotoType.POS_PHOTO.getType()){
if (taskClockDO.getTaskType() == ActivityPhotoType.POS_SELL_VOUCHER.getType()){
res.setEditableFlag(true);
}
}
......
......@@ -91,7 +91,7 @@ public class PromPlanCoreServiceImpl implements PromPlanCoreService {
String[] headers = readExcelUtils.readTitle();
Map<Integer, List<Object>> rows = readExcelUtils.readContent();
if (headers.length != 12) {
if (headers.length != 14) {
throw new DataException(RCode.ACTIVITY_PLAN_TEM_ERROR);
}
if (CollectionUtil.isEmpty(rows)) {
......@@ -102,7 +102,7 @@ public class PromPlanCoreServiceImpl implements PromPlanCoreService {
for (Map.Entry<Integer, List<Object>> row : rows.entrySet()) {
List<Object> cells = row.getValue();
// 判断当前行是否有任一数据为空,如果有则报错,不进行解析
boolean isEmpty = (cells.size() != 12) || cells.stream().anyMatch(cell -> ObjectUtil.isNull(cell) || StringUtils.isBlank(cell.toString()));
boolean isEmpty = (cells.size() != 14) || cells.stream().anyMatch(cell -> ObjectUtil.isNull(cell) || StringUtils.isBlank(cell.toString()));
if (isEmpty) {
indexList.add(row.getKey() + 1);
}
......@@ -171,7 +171,7 @@ public class PromPlanCoreServiceImpl implements PromPlanCoreService {
dto.setErrorMsg("门店编码错误;");
} else {
if (StringUtils.isAnyBlank(storeDto.getDealersName(), storeDto.getDealerId())) {
dto.setErrorMsg("门店「经销商」为空,请到勤策中充;");
dto.setErrorMsg("门店「经销商」为空,请到勤策中充;");
}
dto.setLineName(storeDto.getLineName());
dto.setStoreCode(sc);
......@@ -188,10 +188,10 @@ public class PromPlanCoreServiceImpl implements PromPlanCoreService {
* 3:活动模式
*/
String pattern = row.get(3).toString();
if ("单点CP,常规MINI秀,校园活动".contains(pattern)) {
if ("大区KA,重客KA,大区零食,重客零食,场景营销".contains(pattern)) {
dto.setPattern(pattern);
} else {
dto.setErrorMsg("活动模式分为:单点CP、常规MINI秀、校园活动;");
dto.setErrorMsg("活动模式分为:大区KA、重客KA、大区零食、重客零食、场景营销;");
}
/**
......@@ -248,24 +248,43 @@ public class PromPlanCoreServiceImpl implements PromPlanCoreService {
/**
* 10:是否有促销台/试吃台
* 10:门店是否可用试吃台
*/
String temWlSct = row.get(10).toString();
if ("有,无".contains(temWlSct)) {
dto.setTemWlSct(temWlSct);
String isCanUseTasteCounter = row.get(10).toString();
if ("是,否".contains(isCanUseTasteCounter)) {
dto.setIsCanUseTasteCounter(isCanUseTasteCounter);
} else {
dto.setErrorMsg("是否有促销台请填写:有、无;");
dto.setErrorMsg("门店是否可用试吃台请填写:是、否;");
}
/**
* 11:门店是否已配试吃台
*/
String temWlSct = row.get(11).toString();
if ("是,否".contains(temWlSct)) {
dto.setTemWlSct(temWlSct);
} else {
dto.setErrorMsg("门店是否已配试吃台请填写:是、否;");
}
/**
* 11:活动品项
* 12:活动品项
*/
String prdClass = row.get(11).toString();
if ("黑鸭,散称,老品".contains(prdClass)) {
String prdClass = row.get(12).toString();
if ("黑鸭,散称,老品,礼盒".contains(prdClass)) {
dto.setPrdClass(prdClass);
} else {
dto.setErrorMsg("活动品项请填写:黑鸭、散称、老品;");
dto.setErrorMsg("活动品项请填写:黑鸭、散称、老品、礼盒;");
}
/**
* 13:活动执行模式
*/
String planExecuteMode = row.get(13).toString();
if ("第三方,经销商".contains(planExecuteMode)) {
dto.setPlanExecuteMode(planExecuteMode);
}else {
dto.setErrorMsg("活动执行模式请填写:第三方、经销商;");
}
// 作废:工资
......@@ -325,7 +344,7 @@ public class PromPlanCoreServiceImpl implements PromPlanCoreService {
String[] headers = readExcelUtils.readTitle();
Map<Integer, List<Object>> rows = readExcelUtils.readContent();
if (headers.length != 12) {
if (headers.length != 14) {
throw new DataException(RCode.ACTIVITY_PLAN_TEM_ERROR);
}
if (CollectionUtil.isEmpty(rows)) {
......@@ -336,7 +355,7 @@ public class PromPlanCoreServiceImpl implements PromPlanCoreService {
for (Map.Entry<Integer, List<Object>> row : rows.entrySet()) {
List<Object> cells = row.getValue();
// 判断当前行是否有任一数据为空,如果有则报错,不进行解析
boolean isEmpty = (cells.size() != 12) || cells.stream().anyMatch(cell -> ObjectUtil.isNull(cell) || StringUtils.isBlank(cell.toString()));
boolean isEmpty = (cells.size() != 14) || cells.stream().anyMatch(cell -> ObjectUtil.isNull(cell) || StringUtils.isBlank(cell.toString()));
if (isEmpty) {
indexList.add(row.getKey() + 1);
}
......@@ -409,7 +428,7 @@ public class PromPlanCoreServiceImpl implements PromPlanCoreService {
dto.setErrorMsg("门店编码错误;");
} else {
if (StringUtils.isAnyBlank(storeDto.getDealersName(), storeDto.getDealerId())) {
dto.setErrorMsg("门店「经销商」为空,请到勤策中充;");
dto.setErrorMsg("门店「经销商」为空,请到勤策中充;");
}
dto.setLineName(storeDto.getLineName());
dto.setStoreCode(sc);
......@@ -426,10 +445,10 @@ public class PromPlanCoreServiceImpl implements PromPlanCoreService {
* 3:活动模式
*/
String pattern = row.get(3).toString();
if ("单点CP,常规MINI秀,校园活动".contains(pattern)) {
if ("大区KA,重客KA,大区零食,重客零食,场景营销".contains(pattern)) {
dto.setPattern(pattern);
} else {
dto.setErrorMsg("活动模式分为:单点CP、常规MINI秀、校园活动;");
dto.setErrorMsg("活动模式分为:大区KA、重客KA、大区零食、重客零食、场景营销;");
}
/**
......@@ -482,23 +501,43 @@ public class PromPlanCoreServiceImpl implements PromPlanCoreService {
}
/**
* 10:是否有促销台/试吃台
* 10:门店是否可用试吃台
*/
String isCanUseTasteCounter = row.get(10).toString();
if ("是,否".contains(isCanUseTasteCounter)) {
dto.setIsCanUseTasteCounter(isCanUseTasteCounter);
} else {
dto.setErrorMsg("门店是否可用试吃台请填写:是、否;");
}
/**
* 11:门店是否已配试吃台
*/
String temWlSct = row.get(10).toString();
if ("有,无".contains(temWlSct)) {
String temWlSct = row.get(11).toString();
if ("是,否".contains(temWlSct)) {
dto.setTemWlSct(temWlSct);
} else {
dto.setErrorMsg("是否有促销台请填写:有、无;");
dto.setErrorMsg("门店是否已配试吃台请填写:是、否;");
}
/**
* 11:活动品项
* 12:活动品项
*/
String prdClass = row.get(11).toString();
if ("黑鸭,散称,老品".contains(prdClass)) {
String prdClass = row.get(12).toString();
if ("黑鸭,散称,老品,礼盒".contains(prdClass)) {
dto.setPrdClass(prdClass);
} else {
dto.setErrorMsg("活动品项请填写:黑鸭、散称、老品;");
dto.setErrorMsg("活动品项请填写:黑鸭、散称、老品、礼盒;");
}
/**
* 13:活动执行模式
*/
String planExecuteMode = row.get(13).toString();
if ("第三方,经销商".contains(planExecuteMode)) {
dto.setPlanExecuteMode(planExecuteMode);
}else {
dto.setErrorMsg("活动执行模式请填写:第三方、经销商;");
}
// 8:工资
......@@ -605,8 +644,10 @@ public class PromPlanCoreServiceImpl implements PromPlanCoreService {
.setClockOutTime(operVo.getClockOutTime())
.setNoonClockOutTime(operVo.getNoonClockOutTime())
.setNoonClockInTime(operVo.getNoonClockInTime())
.setIsCanUseTasteCounter(operVo.getIsCanUseTasteCounter())
.setTemWlSct(operVo.getTemWlSct())
.setPrdClass(operVo.getPrdClass())
.setPlanExecuteMode(operVo.getPlanExecuteMode())
.setModifyBy(operVo.getOperName());
activityPlanInfoDao.updateById(planDto);
}
......@@ -641,8 +682,10 @@ public class PromPlanCoreServiceImpl implements PromPlanCoreService {
.setClockOutTime(operVo.getClockOutTime())
.setNoonClockOutTime(operVo.getNoonClockOutTime())
.setNoonClockInTime(operVo.getNoonClockInTime())
.setIsCanUseTasteCounter(operVo.getIsCanUseTasteCounter())
.setTemWlSct(operVo.getTemWlSct())
.setPrdClass(operVo.getPrdClass())
.setPlanExecuteMode(operVo.getPlanExecuteMode())
.setCreateBy(operVo.getOperName());
activityPlanInfoDao.save(planDto);
}
......@@ -673,7 +716,7 @@ public class PromPlanCoreServiceImpl implements PromPlanCoreService {
String[] headers = readExcelUtils.readTitle();
Map<Integer, List<Object>> rows = readExcelUtils.readContent();
if (headers.length != 12) {
if (headers.length != 14) {
throw new DataException(RCode.ACTIVITY_PLAN_TEM_ERROR);
}
if (CollectionUtil.isEmpty(rows)) {
......@@ -684,7 +727,7 @@ public class PromPlanCoreServiceImpl implements PromPlanCoreService {
for (Map.Entry<Integer, List<Object>> row : rows.entrySet()) {
List<Object> cells = row.getValue();
// 判断当前行是否有任一数据为空,如果有则报错,不进行解析
boolean isEmpty = (cells.size() != 12) || cells.stream().anyMatch(cell -> ObjectUtil.isNull(cell) || StringUtils.isBlank(cell.toString()));
boolean isEmpty = (cells.size() != 14) || cells.stream().anyMatch(cell -> ObjectUtil.isNull(cell) || StringUtils.isBlank(cell.toString()));
if (isEmpty) {
indexList.add(row.getKey() + 1);
}
......@@ -747,7 +790,7 @@ public class PromPlanCoreServiceImpl implements PromPlanCoreService {
dto.setErrorMsg("门店编码错误;");
} else {
if (StringUtils.isAnyBlank(storeDto.getDealersName(), storeDto.getDealerId())) {
dto.setErrorMsg("门店「经销商」为空,请到勤策中充;");
dto.setErrorMsg("门店「经销商」为空,请到勤策中充;");
}
dto.setStoreCode(sc);
dto.setStoreName(storeDto.getStoreName());
......@@ -813,32 +856,50 @@ public class PromPlanCoreServiceImpl implements PromPlanCoreService {
* 3:活动模式
*/
String pattern = row.get(3).toString();
if ("单点CP,常规MINI秀,校园活动".contains(pattern)) {
if ("大区KA,重客KA,大区零食,重客零食,场景营销".contains(pattern)) {
dto.setPattern(pattern);
} else {
dto.setErrorMsg("活动模式分为:单点CP、常规MINI秀、校园活动;");
dto.setErrorMsg("活动模式分为:大区KA、重客KA、大区零食、重客零食、场景营销;");
}
/**
* 10:门店是否可用试吃台
*/
String isCanUseTasteCounter = row.get(10).toString();
if ("是,否".contains(isCanUseTasteCounter)) {
dto.setIsCanUseTasteCounter(isCanUseTasteCounter);
} else {
dto.setErrorMsg("门店是否可用试吃台请填写:是、否;");
}
/**
* 10:是否有促销台/试吃台
* 11:门店是否已配试吃台
*/
String temWlSct = row.get(10).toString();
if ("有,无".contains(temWlSct)) {
String temWlSct = row.get(11).toString();
if ("是,否".contains(temWlSct)) {
dto.setTemWlSct(temWlSct);
} else {
dto.setErrorMsg("是否有促销台请填写:有、无;");
dto.setErrorMsg("门店是否已配试吃台请填写:是、否;");
}
/**
* 11:活动品项
* 12:活动品项
*/
String prdClass = row.get(11).toString();
if ("黑鸭,散称,老品".contains(prdClass)) {
String prdClass = row.get(12).toString();
if ("黑鸭,散称,老品,礼盒".contains(prdClass)) {
dto.setPrdClass(prdClass);
} else {
dto.setErrorMsg("活动品项请填写:黑鸭、散称、老品;");
dto.setErrorMsg("活动品项请填写:黑鸭、散称、老品、礼盒;");
}
/**
* 13:活动执行模式
*/
String planExecuteMode = row.get(13).toString();
if ("第三方,经销商".contains(planExecuteMode)) {
dto.setPlanExecuteMode(planExecuteMode);
}else {
dto.setErrorMsg("活动执行模式请填写:第三方、经销商;");
}
// 8:工资
// dto.setSalary(new BigDecimal(row.get(8).toString()));
// 9:杂费
......
package com.wangxiaolu.promotion.service.user;
import com.wangxiaolu.promotion.utils.TencentMapUtil;
import java.util.List;
/**
* @Author: DouXinYu
* @Date: 2026-01-12 11:17
* @Description:
*/
public interface TencentLocationQueryService {
TencentMapUtil.AddressInfo getAddressByLngLat(double lng, double lat);
List<TencentMapUtil.RegionNode> getAddressList();
}
package com.wangxiaolu.promotion.service.user;
import com.wangxiaolu.promotion.domain.user.mapper.entity.WxTemporaryInfoDelayDO;
import com.wangxiaolu.promotion.pojo.activity.temporary.dto.WxTemporaryInfoDelayDtO;
import java.time.LocalDateTime;
import java.util.List;
/**
* 延迟临时表Service(适配前端入参字段)
*/
public interface WxTemporaryInfoDelayService {
/**
* 保存延迟数据到临时表(含storeCode/dataDate)
*/
boolean saveDelayData(WxTemporaryInfoDelayDtO delayEntity);
/**
* 查询指定时间范围未处理的数据
*/
List<WxTemporaryInfoDelayDtO> listUnprocessedData(LocalDateTime startTime, LocalDateTime endTime);
/**
* 批量更新处理状态
*/
boolean batchUpdateProcessedStatus(List<Long> idList, Integer isProcessed);
}
\ No newline at end of file
package com.wangxiaolu.promotion.service.user.impl;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.wangxiaolu.promotion.service.user.TencentLocationQueryService;
import com.wangxiaolu.promotion.utils.TencentMapUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.lang.reflect.Type;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @Author: DouXinYu
* @Date: 2026-01-12 11:16
* @Description: 腾讯位置查询服务实现类
*/
@Service
public class TencentLocationQueryServiceImpl implements TencentLocationQueryService {
private static final String TRIPLE_REGION_CACHE_KEY = "promotion:district:triple_level";
private static final long CACHE_EXPIRE = 86400L;
private static final Type REGION_NODE_TYPE = new TypeToken<List<TencentMapUtil.RegionNode>>() {}.getType();
private static final Gson GSON = new Gson();
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
TencentMapUtil tencentMapUtil;
@Override
public TencentMapUtil.AddressInfo getAddressByLngLat(double lng, double lat) {
return tencentMapUtil.getAddressByLngLat(lng, lat);
}
@Override
public List<TencentMapUtil.RegionNode> getAddressList() {
String cacheJson = stringRedisTemplate.opsForValue().get(TRIPLE_REGION_CACHE_KEY);
if (cacheJson != null && !cacheJson.isEmpty()) {
return GSON.fromJson(cacheJson, REGION_NODE_TYPE);
}
List<TencentMapUtil.RegionNode> regionList = tencentMapUtil.getTripleLevelRegion();
if (!regionList.isEmpty()) {
stringRedisTemplate.opsForValue().set(TRIPLE_REGION_CACHE_KEY, GSON.toJson(regionList), CACHE_EXPIRE, TimeUnit.SECONDS);
}
return regionList;
}
}
package com.wangxiaolu.promotion.service.user.impl;
import com.wangxiaolu.promotion.domain.user.dao.WxTemporaryInfoDelayDao;
import com.wangxiaolu.promotion.domain.user.mapper.entity.WxTemporaryInfoDelayDO;
import com.wangxiaolu.promotion.pojo.activity.temporary.dto.WxTemporaryInfoDelayDtO;
import com.wangxiaolu.promotion.service.user.WxTemporaryInfoDelayService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class WxTemporaryInfoDelayServiceImpl implements WxTemporaryInfoDelayService {
@Resource
private WxTemporaryInfoDelayDao delayDao;
@Override
public boolean saveDelayData(WxTemporaryInfoDelayDtO delayEntity) {
// 调用DAO插入数据(含storeCode/dataDate)
return delayDao.insert(delayEntity) > 0;
}
@Override
public List<WxTemporaryInfoDelayDtO> listUnprocessedData(LocalDateTime startTime, LocalDateTime endTime) {
return delayDao.selectUnprocessedData(startTime, endTime);
}
@Override
public boolean batchUpdateProcessedStatus(List<Long> idList, Integer isProcessed) {
return delayDao.batchUpdateProcessedStatus(idList, isProcessed) > 0;
}
}
\ No newline at end of file
package com.wangxiaolu.promotion.service.wechat;
import com.wangxiaolu.promotion.domain.user.mapper.entity.WxTemporaryInfoDelayDO;
import com.wangxiaolu.promotion.pojo.activity.temporary.dto.WxTemporaryInfoDelayDtO;
import com.wangxiaolu.promotion.pojo.user.dto.WxTemporaryInfoDto;
import java.util.List;
/**
* @author : liqiulin
* @date : 2024-04-08 16
......@@ -13,4 +17,18 @@ public interface WeChatUserCoreService {
* 保存促销员用户信息
*/
boolean saveWxUserInfoTemporary(WxTemporaryInfoDto temporaryDto);
/**
* 修改促销员的门店信息
* @param temporaryDto
* @return
*/
boolean saveWxUserStoreInfoTemporary(WxTemporaryInfoDto temporaryDto);
boolean batchSaveWxUserStoreInfo(List<WxTemporaryInfoDto> list);
/**
* 保存延迟数据
*/
boolean saveDelayData(WxTemporaryInfoDelayDtO delayEntity);
}
......@@ -17,5 +17,15 @@ public interface WeChatUserQueryService {
LoginVo temporaryLoginByPhone(String phone);
List<Map<String, String>> findStoreList(TemporaryRegisterVo temporaryRegisterVo);
List<Map<String, String>> findStoreList(String planDay,String storeNameKeyword);
/**
* 根据临时ID查询手机号
* @param temporaryId 临时ID
* @return 手机号
*/
String findUserPhoneByID(Long temporaryId);
Map<String,Object> findTemporaryWorkStore(String openId,String date);
}
......@@ -5,13 +5,16 @@ import com.wangxiaolu.promotion.domain.user.dao.QinCeEmployeeDao;
import com.wangxiaolu.promotion.domain.user.dao.TemporaryInfoDao;
import com.wangxiaolu.promotion.exception.ParamException;
import com.wangxiaolu.promotion.pojo.activity.temporary.dto.QinCeEmployeeDto;
import com.wangxiaolu.promotion.pojo.activity.temporary.dto.WxTemporaryInfoDelayDtO;
import com.wangxiaolu.promotion.pojo.user.dto.WxTemporaryInfoDto;
import com.wangxiaolu.promotion.result.basedata.RCode;
import com.wangxiaolu.promotion.service.user.WxTemporaryInfoDelayService;
import com.wangxiaolu.promotion.service.wechat.WeChatUserCoreService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
/**
......@@ -29,6 +32,9 @@ public class WeChatUserCoreServiceImpl implements WeChatUserCoreService {
@Autowired
QinCeEmployeeDao qinCeEmployeeDao;
@Autowired
private WxTemporaryInfoDelayService wxTemporaryInfoDelayService;
/**
* 保存促销员用户信息
*/
......@@ -38,4 +44,29 @@ public class WeChatUserCoreServiceImpl implements WeChatUserCoreService {
log.info("微信-促销员[{}]:[{}]注册成功:{}", saveId, temporaryDto.getName(), JSONObject.toJSONString(temporaryDto));
return saveId > 0;
}
/**
* 保存用户门店信息
* @param temporaryDto
* @return
*/
@Override
public boolean saveWxUserStoreInfoTemporary(WxTemporaryInfoDto temporaryDto) {
WxTemporaryInfoDto wxTemporaryInfoDto = temporaryInfoDao.selectOneByOpenId(temporaryDto.getOpenId());
wxTemporaryInfoDto.setStoreCode(temporaryDto.getStoreCode());
wxTemporaryInfoDto.setStoreName(temporaryDto.getStoreName());
int saveId = temporaryInfoDao.saveWxUserStoreInfoTemporary(wxTemporaryInfoDto);
return saveId > 0;
}
@Override
public boolean batchSaveWxUserStoreInfo(List<WxTemporaryInfoDto> list) {
// 调用DAO批量插入/更新(含storeCode/dataDate)
return temporaryInfoDao.batchInsertOrUpdate(list) > 0;
}
@Override
public boolean saveDelayData(WxTemporaryInfoDelayDtO delayEntity) {
return wxTemporaryInfoDelayService.saveDelayData(delayEntity);
}
}
......@@ -71,7 +71,7 @@ public class WeChatUserQueryServiceImpl implements WeChatUserQueryService {
@Override
public LoginVo temporaryLoginByPhone(String phone) {
WxTemporaryInfoDto temDto = temporaryInfoDao.selectByPhone(phone);
WxTemporaryInfoDto temDto = temporaryInfoDao.selectOneByOpenId(phone);
if (Objects.isNull(temDto)) {
throw new DataException(RCode.LOGIN_USER_IS_NULL_ERROR);
}
......@@ -91,7 +91,17 @@ public class WeChatUserQueryServiceImpl implements WeChatUserQueryService {
}
@Override
public List<Map<String, String>> findStoreList(TemporaryRegisterVo temporaryRegisterVo) {
return activityPlanInfoDao.findThisMonthStoreListByLocation(temporaryRegisterVo);
public List<Map<String, String>> findStoreList(String planDay,String storeNameKeyword) {
return activityPlanInfoDao.findThisMonthStoreListByLocation(planDay, storeNameKeyword);
}
@Override
public String findUserPhoneByID(Long temporaryId) {
return temporaryInfoDao.selectOneById(temporaryId).getPhone();
}
@Override
public Map<String,Object> findTemporaryWorkStore(String openId,String date) {
return temporaryInfoDao.selectStoreAndPlan(openId,date);
}
}
package com.wangxiaolu.promotion.utils;
import com.google.gson.Gson;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 腾讯地图工具类(整合:逆地理编码 + 省市区三级嵌套列表)
*/
@Component
public class TencentMapUtil {
// 你的腾讯API Key
private static final String API_KEY = "UTEBZ-UJ3KG-OORQO-QT3PT-JDWU7-YRBZA";
// 1. 逆地理编码接口
private static final String GEOCODER_URL = "https://apis.map.qq.com/ws/geocoder/v1/?location=%f,%f&key=%s&get_poi=0";
// 2. 省市区三级嵌套接口(新版结构)
private static final String DISTRICT_TRIPLE_URL = "https://apis.map.qq.com/ws/district/v1/list?key=%s&struct_type=1";
// OkHttp客户端
private static final OkHttpClient OK_HTTP_CLIENT = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.connectionPool(new ConnectionPool(10, 30, TimeUnit.SECONDS))
.retryOnConnectionFailure(true)
.build();
private static final Gson GSON = new Gson();
@Autowired
private StringRedisTemplate stringRedisTemplate;
// ==================== 1. 逆地理编码实体(保留原有) ====================
public static class AddressInfo {
private String province; // 省份
private String city; // 城市
private String district; // 区县
private String street; // 街道
private String streetNumber;// 门牌号
private String fullAddress; // 完整地址
private double lng; // 经度
private double lat; // 纬度
// Getter & Setter
public String getProvince() { return province; }
public void setProvince(String province) { this.province = province; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
public String getDistrict() { return district; }
public void setDistrict(String district) { this.district = district; }
public String getStreet() { return street; }
public void setStreet(String street) { this.street = street; }
public String getStreetNumber() { return streetNumber; }
public void setStreetNumber(String streetNumber) { this.streetNumber = streetNumber; }
public String getFullAddress() { return fullAddress; }
public void setFullAddress(String fullAddress) { this.fullAddress = fullAddress; }
public double getLng() { return lng; }
public void setLng(double lng) { this.lng = lng; }
public double getLat() { return lat; }
public void setLat(double lat) { this.lat = lat; }
}
// ==================== 2. 三级嵌套列表实体(匹配前端) ====================
public static class RegionNode {
private String text; // 全称(如“北京市”)
private String value; // adcode(如“110000”)
private List<RegionNode> children; // 下级节点
public RegionNode() {
this.children = new ArrayList<>();
}
public RegionNode(String text, String value) {
this.text = text;
this.value = value;
this.children = new ArrayList<>();
}
// Getter & Setter
public String getText() { return text; }
public void setText(String text) { this.text = text; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
public List<RegionNode> getChildren() { return children; }
public void setChildren(List<RegionNode> children) { this.children = children; }
}
// ==================== 腾讯API内部解析实体 ====================
// 逆地理编码响应
private static class GeoResponse {
private int status;
private String message;
private GeoResult result;
// Getter & Setter
public int getStatus() { return status; }
public void setStatus(int status) { this.status = status; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public GeoResult getResult() { return result; }
public void setResult(GeoResult result) { this.result = result; }
}
private static class GeoResult {
private String formatted_address;
private AddressComponent address_component;
// Getter & Setter
public String getFormatted_address() { return formatted_address; }
public void setFormatted_address(String formatted_address) { this.formatted_address = formatted_address; }
public AddressComponent getAddress_component() { return address_component; }
public void setAddress_component(AddressComponent address_component) { this.address_component = address_component; }
}
private static class AddressComponent {
private String province;
private String city;
private String district;
private String street;
private String street_number;
// Getter & Setter
public String getProvince() { return province; }
public void setProvince(String province) { this.province = province; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
public String getDistrict() { return district; }
public void setDistrict(String district) { this.district = district; }
public String getStreet() { return street; }
public void setStreet(String street) { this.street = street; }
public String getStreet_number() { return street_number; }
public void setStreet_number(String street_number) { this.street_number = street_number; }
}
// 三级嵌套响应
private static class DistrictResponse {
private int status;
private String message;
private List<ProvinceItem> result;
// Getter & Setter
public int getStatus() { return status; }
public void setStatus(int status) { this.status = status; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public List<ProvinceItem> getResult() { return result; }
public void setResult(List<ProvinceItem> result) { this.result = result; }
}
private static class ProvinceItem {
private String id;
private String fullname;
private List<CityItem> districts;
// Getter & Setter
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getFullname() { return fullname; }
public void setFullname(String fullname) { this.fullname = fullname; }
public List<CityItem> getDistricts() { return districts; }
public void setDistricts(List<CityItem> districts) { this.districts = districts; }
}
private static class CityItem {
private String id;
private String fullname;
private List<DistrictItem> districts;
// Getter & Setter
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getFullname() { return fullname; }
public void setFullname(String fullname) { this.fullname = fullname; }
public List<DistrictItem> getDistricts() { return districts; }
public void setDistricts(List<DistrictItem> districts) { this.districts = districts; }
}
private static class DistrictItem {
private String id;
private String fullname;
// Getter & Setter
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getFullname() { return fullname; }
public void setFullname(String fullname) { this.fullname = fullname; }
}
// ==================== 核心功能方法 ====================
/**
* 1. 逆地理编码:经纬度转地址(保留原有功能)
*/
public AddressInfo getAddressByLngLat(double lng, double lat) {
AddressInfo addressInfo = new AddressInfo();
try {
// 拼接请求地址(注意:腾讯接口是 lat,lng 顺序)
String requestUrl = String.format(GEOCODER_URL, lat, lng, API_KEY);
Request request = new Request.Builder().url(requestUrl).get().build();
try (Response response = OK_HTTP_CLIENT.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("逆地理编码请求失败,响应码:" + response.code());
}
GeoResponse resp = GSON.fromJson(response.body().string(), GeoResponse.class);
if (resp.getStatus() != 0) {
throw new IOException("逆地理编码API失败:" + resp.getMessage());
}
// 解析地址信息
GeoResult result = resp.getResult();
AddressComponent component = result.getAddress_component();
addressInfo.setProvince(component.getProvince());
addressInfo.setCity(component.getCity());
addressInfo.setDistrict(component.getDistrict());
addressInfo.setStreet(component.getStreet());
addressInfo.setStreetNumber(component.getStreet_number());
addressInfo.setFullAddress(result.getFormatted_address());
addressInfo.setLng(lng);
addressInfo.setLat(lat);
}
} catch (Exception e) {
System.err.println("逆地理编码失败:" + e.getMessage());
e.printStackTrace();
}
return addressInfo;
}
/**
* 2. 获取省→市→区三级嵌套列表(匹配前端)
*/
public List<RegionNode> getTripleLevelRegion() {
List<RegionNode> regionList = new ArrayList<>();
try {
String requestUrl = String.format(DISTRICT_TRIPLE_URL, API_KEY);
Request request = new Request.Builder().url(requestUrl).get().build();
try (Response response = OK_HTTP_CLIENT.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("三级区划请求失败,响应码:" + response.code());
}
DistrictResponse resp = GSON.fromJson(response.body().string(), DistrictResponse.class);
if (resp.getStatus() != 0) {
throw new IOException("三级区划API失败:" + resp.getMessage());
}
// 转换为前端结构
if (resp.getResult() != null && !resp.getResult().isEmpty()) {
for (ProvinceItem province : resp.getResult()) {
RegionNode provinceNode = new RegionNode(province.getFullname(), province.getId());
if (province.getDistricts() != null && !province.getDistricts().isEmpty()) {
for (CityItem city : province.getDistricts()) {
RegionNode cityNode = new RegionNode(city.getFullname(), city.getId());
if (city.getDistricts() != null && !city.getDistricts().isEmpty()) {
for (DistrictItem district : city.getDistricts()) {
cityNode.getChildren().add(new RegionNode(district.getFullname(), district.getId()));
}
}
provinceNode.getChildren().add(cityNode);
}
}
regionList.add(provinceNode);
}
}
}
} catch (Exception e) {
System.err.println("生成三级区划失败:" + e.getMessage());
e.printStackTrace();
}
return regionList;
}
// // ==================== 缓存预热 ====================
// @PostConstruct
// public void preloadCache() {
// // 预热三级区划缓存
// new Thread(() -> {
// try {
// List<RegionNode> regionList = getTripleLevelRegion();
// if (!regionList.isEmpty()) {
// stringRedisTemplate.opsForValue().set(
// "promotion:district:triple_level",
// GSON.toJson(regionList),
// 86400L,
// TimeUnit.SECONDS
// );
// }
// } catch (Exception e) {
// System.err.println("三级区划缓存预热失败:" + e.getMessage());
// }
// }).start();
// }
}
\ No newline at end of file
......@@ -162,7 +162,7 @@ public class ActivityStautsHandler {
// 模板中的字段3 任务名称
data.put("thing1", "请拍摄清晰的工作场景照片");
// 模板中的字段4 温馨提示
data.put("thing4", "请在15分钟内完成任务");
data.put("thing4", "请在30分钟内完成任务");
// 3. 发送通知
// 获取openid
......
package com.wangxiaolu.promotion.xxljobtask;
import com.wangxiaolu.promotion.pojo.activity.temporary.dto.WxTemporaryInfoDelayDtO;
import com.wangxiaolu.promotion.pojo.user.dto.WxTemporaryInfoDto;
import com.wangxiaolu.promotion.service.wechat.WeChatUserCoreService;
import com.wangxiaolu.promotion.service.user.WxTemporaryInfoDelayService;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* XXL-Job定时任务:每天00:01处理微信用户延迟更新的门店信息
* @author: 自定义
* @date: 2026-01-14
*/
@Slf4j
@Component
public class WxTemporaryInfoDelayHandler{
@Resource
private WxTemporaryInfoDelayService wxTemporaryInfoDelayService;
@Resource
private WeChatUserCoreService weChatUserCoreService;
/**
* 定时任务执行方法
* 任务描述:每天00:01处理前一天未处理的用户门店信息更新
* XXL-Job配置:cron表达式 0 1 0 * * ? (每天00:01执行)
*/
@XxlJob("wxTemporaryInfoDelayHandler")
public void execute() {
// 1. 任务日志记录(XXL-Job控制台可见)
XxlJobHelper.log("开始执行微信用户延迟信息更新任务,执行时间:{}", LocalDateTime.now());
try {
// 2. 构建时间范围:查询前一天00:00到当天00:00的未处理数据
LocalDate yesterday = LocalDate.now().minusDays(1);
LocalDateTime startTime = LocalDateTime.of(yesterday, LocalTime.MIN);
LocalDateTime endTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
// 3. 查询未处理的延迟数据
List<WxTemporaryInfoDelayDtO> unprocessedList = wxTemporaryInfoDelayService.listUnprocessedData(startTime, endTime);
if (unprocessedList == null || unprocessedList.isEmpty()) {
XxlJobHelper.log("未查询到需要处理的延迟数据,任务结束");
return;
}
XxlJobHelper.log("查询到待处理延迟数据共{}条", unprocessedList.size());
// 4. 转换DTO并批量更新门店信息
List<WxTemporaryInfoDto> updateList = new ArrayList<>();
List<Long> idList = new ArrayList<>();
for (WxTemporaryInfoDelayDtO delayDto : unprocessedList) {
// 4.1 收集需要标记为已处理的ID
idList.add(delayDto.getId());
// 4.2 转换为业务层需要的DTO
WxTemporaryInfoDto tempDto = new WxTemporaryInfoDto();
BeanUtils.copyProperties(delayDto, tempDto);
updateList.add(tempDto);
}
// 5. 批量更新用户门店信息
boolean updateResult = weChatUserCoreService.batchSaveWxUserStoreInfo(updateList);
if (!updateResult) {
XxlJobHelper.log("批量更新用户门店信息失败,数据量:{}", updateList.size());
XxlJobHelper.handleFail("批量更新用户门店信息失败");
return;
}
XxlJobHelper.log("批量更新用户门店信息成功,数据量:{}", updateList.size());
// 6. 标记延迟表数据为已处理(1=已处理)
boolean markResult = wxTemporaryInfoDelayService.batchUpdateProcessedStatus(idList, 1);
if (!markResult) {
XxlJobHelper.log("标记延迟数据为已处理失败,ID列表:{}", idList);
XxlJobHelper.handleFail("标记延迟数据为已处理失败");
return;
}
XxlJobHelper.log("标记{}条延迟数据为已处理成功", idList.size());
// 7. 任务执行成功
XxlJobHelper.handleSuccess("微信用户延迟信息更新任务执行成功,共处理" + unprocessedList.size() + "条数据");
} catch (Exception e) {
// 8. 异常处理
log.error("微信用户延迟信息更新任务执行异常", e);
XxlJobHelper.log("任务执行异常:{}", e.getMessage());
XxlJobHelper.handleFail("任务执行异常:" + e.getMessage());
}
}
}
\ No newline at end of file
......@@ -18,6 +18,8 @@
<result property="idenReversePhotoFieldId" column="iden_reverse_photo_field_id" jdbcType="VARCHAR"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="modifyTime" column="modify_time" jdbcType="TIMESTAMP"/>
<result property="storeName" column="store_name" jdbcType="VARCHAR"/>
<result property="storeCode" column="store_code" jdbcType="VARCHAR"/>
</resultMap>
<sql id="Base_Column_List">
......@@ -37,4 +39,20 @@
select <include refid="Base_Column_Unimportant"/> from temporary_info where open_id = #{openId}
</select>
<insert id="batchInsertOrUpdate">
INSERT INTO temporary_info (
open_id, store_name, store_code, modify_time
) VALUES
<foreach collection="list" item="item" separator=",">
(
#{item.openId}, #{item.storeName}, #{item.storeCode}, NOW()
)
</foreach>
ON DUPLICATE KEY UPDATE
store_name = VALUES(store_name), <!-- 仅更新门店名称 -->
store_code = VALUES(store_code), <!-- 仅更新门店编码 -->
modify_time = NOW() <!-- 仅更新修改时间 -->
</insert>
</mapper>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wangxiaolu.promotion.domain.user.mapper.WxTemporaryInfoDelayMapper">
<!-- 核心:resultMap映射(适配已有实体类WxTemporaryInfoDelay) -->
<resultMap id="WxTemporaryInfoDelayResultMap" type="com.wangxiaolu.promotion.domain.user.mapper.entity.WxTemporaryInfoDelayDO">
<id column="id" property="id"/> <!-- 主键映射 -->
<result column="open_id" property="openId"/> <!-- 数据库下划线 → Java驼峰 -->
<result column="store_name" property="storeName"/>
<result column="store_code" property="storeCode"/>
<result column="contact_phone" property="contactPhone"/>
<result column="create_time" property="createTime"/>
<result column="is_processed" property="isProcessed"/>
<result column="update_time" property="updateTime"/>
</resultMap>
<!-- 查询指定时间范围未处理的数据(使用resultMap) -->
<select id="selectByTimeAndStatus" resultMap="WxTemporaryInfoDelayResultMap">
SELECT
id, open_id, store_name, store_code, contact_phone,
create_time, is_processed, update_time
FROM wx_temporary_info_delay
WHERE create_time BETWEEN #{startTime} AND #{endTime}
AND is_processed = #{isProcessed}
</select>
<!-- 批量更新处理状态 -->
<update id="batchUpdateStatus">
UPDATE wx_temporary_info_delay
SET is_processed = #{isProcessed}, update_time = NOW()
WHERE id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</update>
<!-- 单条插入(适配已有实体,可选:MyBatis-Plus也可自动生成) -->
<insert id="insert" parameterType="com.wangxiaolu.promotion.domain.user.mapper.entity.WxTemporaryInfoDelayDO">
INSERT INTO wx_temporary_info_delay (
open_id, store_name, store_code, contact_phone,
create_time, is_processed, update_time
) VALUES (
#{openId}, #{storeName}, #{storeCode}, #{contactPhone},
NOW(), 0, NOW()
)
</insert>
</mapper>
\ No newline at end of file
......@@ -68,7 +68,7 @@
<insert id="saveList">
INSERT INTO activity_plan_info
(plan_file_id,employee_id,employee_name,employee_no,year,month,date,line_name,store_name,store_code,org_qc_id,org_name,pattern_id,pattern,dealer_id,dealer_name,both_t,province,city,area,addr,clock_in_time,clock_out_time,noon_clock_out_time,noon_clock_in_time,tem_wl_sct,prd_class,create_by)
(plan_file_id,employee_id,employee_name,employee_no,year,month,date,line_name,store_name,store_code,org_qc_id,org_name,pattern_id,pattern,dealer_id,dealer_name,both_t,province,city,area,addr,clock_in_time,clock_out_time,noon_clock_out_time,noon_clock_in_time,tem_wl_sct,prd_class,create_by,plan_execute_mode,is_can_use_taste_counter)
VALUES
<foreach collection="table" item="item" index="index" separator=",">
(#{recordId}, #{item.employeeId}, #{item.employeeName}, #{item.employeeNo}, #{item.year},
......@@ -76,7 +76,7 @@
#{item.orgQcId},#{item.orgName}, 0, #{item.pattern}, #{item.dealerId}, #{item.dealerName}, '未确定',
#{item.province}, #{item.city}, #{item.area}, #{item.addr},
FROM_UNIXTIME(#{item.clockInTime} / 1000), FROM_UNIXTIME(#{item.clockOutTime} / 1000), FROM_UNIXTIME(#{item.noonClockOutTime} / 1000),
FROM_UNIXTIME(#{item.noonClockInTime} / 1000),#{item.temWlSct},#{item.prdClass},#{item.createBy})
FROM_UNIXTIME(#{item.noonClockInTime} / 1000),#{item.temWlSct},#{item.prdClass},#{item.createBy}, #{item.planExecuteMode}, #{item.isCanUseTasteCounter})
</foreach>
</insert>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论