考勤数据
This commit is contained in:
		| @ -196,7 +196,5 @@ public class SubConstructionUser extends BaseEntity { | ||||
|      */ | ||||
|     private String remark; | ||||
|  | ||||
|  | ||||
|     private LocalDate firstDate; | ||||
|  | ||||
| } | ||||
|  | ||||
| @ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers; | ||||
| import jakarta.annotation.Resource; | ||||
| import jakarta.validation.constraints.NotNull; | ||||
| import org.dromara.common.core.domain.R; | ||||
| import org.dromara.common.idempotent.annotation.RepeatSubmit; | ||||
| import org.dromara.common.mybatis.core.page.PageQuery; | ||||
| import org.dromara.common.mybatis.core.page.TableDataInfo; | ||||
| import org.dromara.common.satoken.utils.LoginHelper; | ||||
| @ -31,8 +32,10 @@ import org.springframework.web.bind.annotation.*; | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
|  | ||||
| import java.time.LocalDate; | ||||
| import java.time.temporal.ChronoUnit; | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
| import java.util.concurrent.TimeUnit; | ||||
|  | ||||
| /** | ||||
|  * app考勤 | ||||
| @ -61,6 +64,7 @@ public class BusAttendanceAppController extends BaseController { | ||||
|     /** | ||||
|      * 人脸坐标打卡 | ||||
|      */ | ||||
|     @RepeatSubmit(interval = 3, timeUnit = TimeUnit.SECONDS,message = "3分钟内禁止重复打卡") | ||||
|     @PostMapping("/punch/card/face") | ||||
|     public R<Boolean> punchCardByFace(@RequestPart("file") MultipartFile file, BusAttendancePunchCardByFaceReq req) { | ||||
|         return R.ok(attendanceService.punchCardByFace(file, req)); | ||||
| @ -183,8 +187,14 @@ public class BusAttendanceAppController extends BaseController { | ||||
|         SubConstructionUser bySysUserId = constructionUserService.getBySysUserId(userId); | ||||
|         if(bySysUserId == null || bySysUserId.getFirstDate() ==  null){ | ||||
|             daysCountVo.setEntryDays(0); | ||||
|         }else{ | ||||
|             daysCountVo.setEntryDays(LocalDate.now().getDayOfYear() - bySysUserId.getFirstDate().getDayOfYear()); | ||||
|         } else { | ||||
|             LocalDate firstDate = bySysUserId.getFirstDate(); | ||||
|             if (firstDate.isAfter(LocalDate.now())) { | ||||
|                 daysCountVo.setEntryDays(0); // 防止未来日期导致负数 | ||||
|             } else { | ||||
|                 long daysDifference = ChronoUnit.DAYS.between(firstDate, LocalDate.now()); | ||||
|                 daysCountVo.setEntryDays((int) daysDifference); | ||||
|             } | ||||
|         } | ||||
|         List<BusAttendance> list = attendanceService.list(Wrappers.<BusAttendance>lambdaQuery() | ||||
|             .eq(BusAttendance::getUserId, userId) | ||||
|  | ||||
| @ -157,4 +157,8 @@ public interface IBusUserProjectRelevancyService extends IService<BusUserProject | ||||
|      */ | ||||
|     Page<BusUserProjectRelevancyVo> getVoPage(Page<BusUserProjectRelevancy> userProjectRelevancyPage); | ||||
|  | ||||
|     /** | ||||
|      * 判断用户是否是施工人员 | ||||
|      */ | ||||
|     Boolean isConstruct(Long userId,Long projectId); | ||||
| } | ||||
|  | ||||
| @ -403,14 +403,62 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B | ||||
|     public Boolean checkInRange(BusAttendancePunchCardByFaceReq req) { | ||||
|         // 获取当前用户 | ||||
|         Long userId = LoginHelper.getUserId(); | ||||
|         List<String> punchRangeList = getPunchRangeByProjectIdAndUserId(req.getProjectId(), userId); | ||||
|         Long projectId = req.getProjectId(); | ||||
|  | ||||
|         //判断是否要求范围内打卡 | ||||
|         BusUserProjectRelevancy relevancy = userProjectRelevancyService.getOne(Wrappers.lambdaQuery(BusUserProjectRelevancy.class) | ||||
|             .eq(BusUserProjectRelevancy::getUserId, userId) | ||||
|             .eq(BusUserProjectRelevancy::getProjectId, projectId) | ||||
|             .last("limit 1")); | ||||
|         if (relevancy == null) { | ||||
|             throw new ServiceException("当前用户未加入项目", HttpStatus.BAD_REQUEST); | ||||
|         } | ||||
|         //判断是否是施工员 管理员返回项目全部打卡范围,施工人员返回班组打卡范围 | ||||
|         boolean isConstruct = "1".equals(relevancy.getUserType()); | ||||
|         List<Long> rangeIds = new ArrayList<>(); | ||||
|         if (isConstruct) { | ||||
|             BusProjectTeamMember one = projectTeamMemberService.getOne(Wrappers.lambdaQuery(BusProjectTeamMember.class) | ||||
|                 .eq(BusProjectTeamMember::getMemberId, userId) | ||||
|                 .eq(BusProjectTeamMember::getProjectId, projectId) | ||||
|                 .last("limit 1")); | ||||
|             if (one == null) { | ||||
|                 throw new ServiceException("当前用户未加入班组", HttpStatus.BAD_REQUEST); | ||||
|             } | ||||
|  | ||||
|             BusProjectTeam team = projectTeamService.getById(one.getTeamId()); | ||||
|             //需要考虑班组不设置考勤范围 | ||||
|             if("1".equals(team.getIsClockIn())){ | ||||
|                 return true; | ||||
|             } | ||||
|             try { | ||||
|                 JSONArray jsonArray = JSONUtil.parseArray(team.getPunchRange()); | ||||
|                 rangeIds = jsonArray.toList(Long.class); | ||||
|             } catch (Exception e) { | ||||
|  | ||||
|             } | ||||
|         } | ||||
|         // 再获取项目的规则 | ||||
|         BusAttendanceRuleVo busAttendanceRuleVo = attendanceRuleService.queryByProjectId(projectId); | ||||
|         if(busAttendanceRuleVo != null && "2".equals(busAttendanceRuleVo.getType())){ | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         List<String> punchRangeList = projectPunchrangeService.lambdaQuery() | ||||
|             .in(CollectionUtil.isNotEmpty(rangeIds), BusProjectPunchrange::getId, rangeIds) | ||||
|             .eq(BusProjectPunchrange::getProjectId, projectId) | ||||
|             .list() | ||||
|             .stream() | ||||
|             .map(BusProjectPunchrange::getPunchRange) | ||||
|             .toList(); | ||||
|  | ||||
|         if (CollUtil.isEmpty(punchRangeList)) { | ||||
|             throw new ServiceException("项目未配置考勤范围", HttpStatus.BAD_REQUEST); | ||||
|             throw new ServiceException(isConstruct?"班组":"项目"+"未配置考勤范围", HttpStatus.BAD_REQUEST); | ||||
|         } | ||||
|         List<GeoPoint> matchingRange = JSTUtil.findMatchingRange(req.getLat(), req.getLng(), punchRangeList); | ||||
|         return matchingRange != null; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     @Override | ||||
|     public List<BusAttendanceVo> getTodayAttendance(Long projectId) { | ||||
|  | ||||
|  | ||||
| @ -162,7 +162,7 @@ public class BusProjectTeamMemberServiceImpl extends ServiceImpl<BusProjectTeamM | ||||
|             .set(SubConstructionUser::getEntryDate, new Date()) | ||||
|             .set(SubConstructionUser::getLeaveDate, null) | ||||
|             .set(SubConstructionUser::getExitStatus, "0") | ||||
|             .set(constructionUser.getFirstDate()!=null,SubConstructionUser::getFirstDate, LocalDate.now()) | ||||
|             .set(constructionUser.getFirstDate()==null,SubConstructionUser::getFirstDate, LocalDate.now()) | ||||
|             ; | ||||
|         constructionUserService.update(constructionUserLuw); | ||||
|  | ||||
|  | ||||
| @ -382,6 +382,10 @@ public class BusReissueCardServiceImpl extends ServiceImpl<BusReissueCardMapper, | ||||
|             BusAttendance byId = attendanceService.getById(attendanceId); | ||||
|             byId.setClockStatus(BusAttendanceClockStatusEnum.REISSUE.getValue()); | ||||
|         } | ||||
|         if("3".equals(bean.getManagerOpinion())){ | ||||
|             BusAttendance byId = attendanceService.getById(attendanceId); | ||||
|             byId.setHandle("0"); | ||||
|         } | ||||
|         return b; | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -460,4 +460,13 @@ public class BusUserProjectRelevancyServiceImpl extends ServiceImpl<BusUserProje | ||||
|         return userProjectRelevancyVoPage; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Boolean isConstruct(Long userId,Long projectId) { | ||||
|         List<BusUserProjectRelevancy> list = this.list(Wrappers.lambdaQuery(BusUserProjectRelevancy.class) | ||||
|             .eq(BusUserProjectRelevancy::getUserId, userId) | ||||
|             .eq(BusUserProjectRelevancy::getProjectId, projectId) | ||||
|         ); | ||||
|  | ||||
|         return list.stream().allMatch(relevancy -> "1".equals(relevancy.getUserType())); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,189 @@ | ||||
| package org.dromara.transferData.controller; | ||||
|  | ||||
| import cn.hutool.core.collection.CollectionUtil; | ||||
| import com.baomidou.mybatisplus.core.toolkit.Wrappers; | ||||
| import jakarta.activation.MimetypesFileTypeMap; | ||||
| import jakarta.annotation.Resource; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
| import org.dromara.project.domain.vo.BusAttendanceRuleVo; | ||||
| import org.dromara.transferData.domain.ConstructionUserCopy; | ||||
| import org.dromara.transferData.domain.OldAttendance; | ||||
| import org.dromara.transferData.mapper.TransferDataMapper; | ||||
| import org.dromara.contractor.service.ISubConstructionUserService; | ||||
| import org.dromara.project.domain.BusAttendance; | ||||
| import org.dromara.project.service.IBusAttendanceRuleService; | ||||
| import org.dromara.project.service.IBusAttendanceService; | ||||
| import org.dromara.system.domain.vo.SysOssVo; | ||||
| import org.dromara.system.service.ISysOssService; | ||||
| import org.springframework.web.bind.annotation.RequestMapping; | ||||
| import org.springframework.web.bind.annotation.RestController; | ||||
|  | ||||
| import java.io.InputStream; | ||||
| import java.net.URI; | ||||
| import java.net.http.HttpClient; | ||||
| import java.net.http.HttpResponse; | ||||
| import java.net.http.HttpRequest; | ||||
| import java.nio.file.Paths; | ||||
| import java.time.LocalDate; | ||||
| import java.time.LocalDateTime; | ||||
| import java.time.LocalTime; | ||||
| import java.time.format.DateTimeParseException; | ||||
| import java.time.temporal.ChronoUnit; | ||||
| import java.util.List; | ||||
|  | ||||
| @RestController | ||||
| @RequestMapping("/transferData") | ||||
| @Slf4j | ||||
| public class TransferDataController { | ||||
|  | ||||
|     @Resource | ||||
|     private TransferDataMapper transferDataMapper; | ||||
|     @Resource | ||||
|     private IBusAttendanceService attendanceService; | ||||
|     @Resource | ||||
|     private IBusAttendanceRuleService attendanceRuleService; | ||||
|     @Resource | ||||
|     private ISubConstructionUserService constructionUserService; | ||||
|     @Resource | ||||
|     private ISysOssService ossService; | ||||
|  | ||||
|     // 两个候选基础URL | ||||
|     private static final String[] BASE_URLS = { | ||||
|         "http://xny.yj-3d.com:7464", | ||||
|         "http://xny.yj-3d.com:7363" | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     @RequestMapping("/transferAttendance") | ||||
|     public void transferAttendance() { | ||||
|         List<OldAttendance> data = transferDataMapper.getData(); | ||||
|         for (OldAttendance oldAttendance : data) { | ||||
|             ConstructionUserCopy constructionUserCopy = transferDataMapper.getConstructionUserCopy(oldAttendance.getOpenid()); | ||||
|  | ||||
|             if (constructionUserCopy == null) { | ||||
|                 continue; | ||||
|             } | ||||
|             LocalDate clockDate = LocalDate.parse(oldAttendance.getPrintingDate()); | ||||
|             //判定是否重读 | ||||
|             List<BusAttendance> list = attendanceService.list(Wrappers.lambdaQuery(BusAttendance.class) | ||||
|                 .eq(BusAttendance::getUserId, constructionUserCopy.getSysUserId()) | ||||
|                 .eq(BusAttendance::getProjectId, constructionUserCopy.getProjectId()) | ||||
|                 .eq(BusAttendance::getClockDate, clockDate) | ||||
|             ); | ||||
|             if(CollectionUtil.isNotEmpty(list)){ | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             BusAttendance busAttendance = new BusAttendance(); | ||||
|  | ||||
|             if(oldAttendance.getPacePhoto()!=null){ | ||||
|                 Long l = handleFaceImage(oldAttendance.getPacePhoto()); | ||||
|                 busAttendance.setFacePic(l==null?"":l.toString()); | ||||
|             } | ||||
|  | ||||
|             busAttendance.setUserName(constructionUserCopy.getUserName()); | ||||
|             busAttendance.setUserId(constructionUserCopy.getSysUserId()); | ||||
|             busAttendance.setProjectId(constructionUserCopy.getProjectId()); | ||||
|  | ||||
|             // 转换日期字段 | ||||
|             busAttendance.setClockDate(clockDate); | ||||
|  | ||||
|             //状态 | ||||
|             busAttendance.setClockStatus(oldAttendance.getIsPinch()); | ||||
|             //类型 | ||||
|             busAttendance.setClockType(oldAttendance.getCommuter()); | ||||
|             //位置 | ||||
|             busAttendance.setClockLocation(oldAttendance.getLocation()); | ||||
|             busAttendance.setLng(oldAttendance.getLng()); | ||||
|             busAttendance.setLat(oldAttendance.getLat()); | ||||
|             //打卡时间 | ||||
|             if (!"4".equals(oldAttendance.getIsPinch())) { | ||||
|                 busAttendance.setClockTime(parseClockOn(oldAttendance.getClockOn(), busAttendance)); | ||||
|             } | ||||
|             //规则和迟到早退时间计算 | ||||
|             BusAttendanceRuleVo busAttendanceRuleVo = attendanceRuleService.queryByProjectId(busAttendance.getProjectId()); | ||||
|             if (busAttendanceRuleVo != null) { | ||||
|                 LocalTime clockInTime = busAttendanceRuleVo.getClockInTime(); | ||||
|                 LocalTime clockOutTime = busAttendanceRuleVo.getClockOutTime(); | ||||
|                 if("1".equals(busAttendance.getClockType())){ | ||||
|                     busAttendance.setRuleTime(clockInTime); | ||||
|                     if("2".equals(oldAttendance.getIsPinch())){ | ||||
|                         LocalDateTime ruleDateTime = LocalDateTime.of(busAttendance.getClockDate(), clockInTime); | ||||
|                         long minutesDiff = ChronoUnit.MINUTES.between(ruleDateTime, busAttendance.getClockTime()); | ||||
|                         long absMinutes = Math.abs(minutesDiff); | ||||
|                         busAttendance.setMinuteCount((int)absMinutes); | ||||
|                     } | ||||
|                 }else { | ||||
|                     busAttendance.setRuleTime(clockOutTime); | ||||
|                     if("3".equals(oldAttendance.getIsPinch())){ | ||||
|                         LocalDateTime ruleDateTime = LocalDateTime.of(busAttendance.getClockDate(), clockOutTime); | ||||
|                         long minutesDiff = ChronoUnit.MINUTES.between(ruleDateTime, busAttendance.getClockTime()); | ||||
|                         long absMinutes = Math.abs(minutesDiff); | ||||
|                         busAttendance.setMinuteCount((int)absMinutes); | ||||
|                     } | ||||
|                 } | ||||
|                 busAttendance.setHandle("5".equals(oldAttendance.getIsPinch())?"1":"0"); | ||||
|             } | ||||
|  | ||||
|         } | ||||
|  | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public Long handleFaceImage(String relativePath) { | ||||
|         String normalizedPath = relativePath.replace("\\", "/"); | ||||
|         String filename = Paths.get(normalizedPath).getFileName().toString(); | ||||
|  | ||||
|         // 使用 MimetypesFileTypeMap 解析 | ||||
|         MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap(); | ||||
|         String contentType = fileTypeMap.getContentType(filename); | ||||
|  | ||||
|         for (String baseUrl : BASE_URLS) { | ||||
|             String fullUrl = baseUrl + normalizedPath; | ||||
|             try { | ||||
|                 HttpClient client = HttpClient.newHttpClient(); | ||||
|                 HttpRequest request = HttpRequest.newBuilder() | ||||
|                     .uri(URI.create(fullUrl)) | ||||
|                     .GET() | ||||
|                     .build(); | ||||
|  | ||||
|                 HttpResponse<InputStream> response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()); | ||||
|  | ||||
|                 if (response.statusCode() == 200) { | ||||
|                     long contentLength = response.headers().firstValueAsLong("Content-Length").orElse(-1); | ||||
|                     SysOssVo ossVo = ossService.upload(response.body(), filename, contentType, contentLength); | ||||
|                     return ossVo.getOssId(); | ||||
|                 } | ||||
|             } catch (Exception e) { | ||||
|                 log.warn("尝试URL失败: {}", fullUrl, e); | ||||
|                 continue; | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     private LocalDateTime parseClockOn(String clockOn, BusAttendance busAttendance) { | ||||
|         if (clockOn == null || "缺卡".equals(clockOn)) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             // 1. 尝试完整日期时间解析(如 "2023-10-14 07:35:07") | ||||
|             return LocalDateTime.parse(clockOn); | ||||
|         } catch (DateTimeParseException e) { | ||||
|             try { | ||||
|                 // 2. 仅时间解析(如 "5:38:00")并结合已有的日期 | ||||
|                 LocalDate date = busAttendance.getClockDate(); | ||||
|                 if (date == null) { | ||||
|                     return null; // 日期不存在时返回 null | ||||
|                 } | ||||
|                 LocalTime time = LocalTime.parse(clockOn); | ||||
|                 return LocalDateTime.of(date, time); | ||||
|             } catch (DateTimeParseException ex) { | ||||
|                 log.warn("无法解析打卡时间: {}", clockOn); | ||||
|                 return null; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,14 @@ | ||||
| package org.dromara.transferData.domain; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| @Data | ||||
| public class ConstructionUserCopy { | ||||
|     private Long id; | ||||
|  | ||||
|     private Long sysUserId; | ||||
|  | ||||
|     private Long projectId; | ||||
|  | ||||
|     private String userName; | ||||
| } | ||||
| @ -0,0 +1,131 @@ | ||||
| package org.dromara.transferData.domain; | ||||
|  | ||||
| import lombok.Data; | ||||
| import java.math.BigDecimal; | ||||
| import java.time.LocalDateTime; | ||||
|  | ||||
| /** | ||||
|  * 打卡记录实体类 | ||||
|  */ | ||||
| @Data | ||||
| public class OldAttendance { | ||||
|     /** | ||||
|      * 主键ID | ||||
|      */ | ||||
|     private Long id; | ||||
|  | ||||
|     /** | ||||
|      * 人员姓名 | ||||
|      */ | ||||
|     private String userName; | ||||
|  | ||||
|     /** | ||||
|      * 人脸照 | ||||
|      */ | ||||
|     private String pacePhoto; | ||||
|  | ||||
|     /** | ||||
|      * 项目id | ||||
|      */ | ||||
|     private Long projectId; | ||||
|  | ||||
|     /** | ||||
|      * 创建者 | ||||
|      */ | ||||
|     private String createBy; | ||||
|  | ||||
|     /** | ||||
|      * 更新者 | ||||
|      */ | ||||
|     private String updateBy; | ||||
|  | ||||
|     /** | ||||
|      * 创建时间 | ||||
|      */ | ||||
|     private LocalDateTime createdAt; | ||||
|  | ||||
|     /** | ||||
|      * 更新时间 | ||||
|      */ | ||||
|     private LocalDateTime updatedAt; | ||||
|  | ||||
|     /** | ||||
|      * 删除时间 | ||||
|      */ | ||||
|     private LocalDateTime deletedAt; | ||||
|  | ||||
|     /** | ||||
|      * 上午打卡 | ||||
|      */ | ||||
|     private String clockOn; | ||||
|  | ||||
|     /** | ||||
|      * 下午打卡 | ||||
|      */ | ||||
|     private String clockOff; | ||||
|  | ||||
|     /** | ||||
|      * 年月日打卡时间 | ||||
|      */ | ||||
|     private String printingDate; | ||||
|  | ||||
|     /** | ||||
|      * 打卡状态:1正常,2迟到,3早退,4缺勤,5补卡 | ||||
|      */ | ||||
|     private String isPinch; | ||||
|  | ||||
|     /** | ||||
|      * 微信id | ||||
|      */ | ||||
|     private String openid; | ||||
|  | ||||
|     /** | ||||
|      * 代打id | ||||
|      */ | ||||
|     private String pinchOpenId; | ||||
|  | ||||
|     /** | ||||
|      * 多次打卡时间记录 | ||||
|      */ | ||||
|     private String clockRecord; | ||||
|  | ||||
|     /** | ||||
|      * 代打人姓名 | ||||
|      */ | ||||
|     private String pinchUserName; | ||||
|  | ||||
|     /** | ||||
|      * 上下班(1上班2下班) | ||||
|      */ | ||||
|     private String commuter; | ||||
|  | ||||
|     /** | ||||
|      * 打卡范围 | ||||
|      */ | ||||
|     private String punchRange; | ||||
|  | ||||
|     /** | ||||
|      * 日薪 | ||||
|      */ | ||||
|     private BigDecimal dailyWage; | ||||
|  | ||||
|     /** | ||||
|      * 经度 | ||||
|      */ | ||||
|     private String lng; | ||||
|  | ||||
|     /** | ||||
|      * 纬度 | ||||
|      */ | ||||
|     private String lat; | ||||
|  | ||||
|     /** | ||||
|      * 逆编码地址信息 | ||||
|      */ | ||||
|     private String location; | ||||
|  | ||||
|     /** | ||||
|      * 缺卡统一处理时间 | ||||
|      */ | ||||
|     private LocalDateTime missing; | ||||
| } | ||||
| @ -0,0 +1,24 @@ | ||||
| package org.dromara.transferData.mapper; | ||||
|  | ||||
| import com.baomidou.dynamic.datasource.annotation.DS; | ||||
| import org.apache.ibatis.annotations.Mapper; | ||||
| import org.apache.ibatis.annotations.Param; | ||||
| import org.apache.ibatis.annotations.Select; | ||||
| import org.dromara.transferData.domain.ConstructionUserCopy; | ||||
| import org.dromara.transferData.domain.OldAttendance; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
|  | ||||
| @Mapper | ||||
| public interface TransferDataMapper { | ||||
|  | ||||
|  | ||||
|     @DS("slave") | ||||
|     @Select("select * from bus_attendance") | ||||
|     List<OldAttendance> getData(); | ||||
|  | ||||
|  | ||||
|     @Select("select id,sys_user_id,project_id,user_name from sub_construction_user_copy1 where go_openid = #{openId}") | ||||
|     ConstructionUserCopy getConstructionUserCopy(@Param("openId") String openId); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 zt
					zt