Browse Source

计划bug处理

dev-v1.0.2
庄锐波 3 months ago
parent
commit
5c5b14f070
  1. 2
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EfcodeCpPlanMapper.java
  2. 2
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EfcodeCpReserveMapper.java
  3. 2
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpPlanServiceImpl.java
  4. 2
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpReserveServiceImpl.java
  5. 8
      zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeCpPlanMapper.xml
  6. 8
      zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeCpReserveMapper.xml
  7. 9
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/plan/cp_plan_other.vue
  8. 26
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_manual.vue
  9. 32
      zdxt-web-server/zdxt-admin-web-server/src/main/java/org/dromara/web/config/WebInterceptorConfig.java
  10. 77
      zdxt-web-server/zdxt-admin-web-server/src/main/java/org/dromara/web/interceptor/HttpRequestLoggingInterceptor.java
  11. 102
      zdxt-web-server/zdxt-admin-web-server/src/main/java/org/dromara/web/interceptor/UserAccessRestrictionInterceptor.java

2
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EfcodeCpPlanMapper.java

@ -21,7 +21,7 @@ public interface EfcodeCpPlanMapper extends BaseMapperPlus<EfcodeCpPlan, EfcodeC
Page<EfcodeCpPlanVo> selectOtherPageList(
@Param("page") Page<EfcodeCpPlanVo> page,
@Param("bo") EfcodeCpPlanBo bo,
@Param("currentUserId") Long currentUserId,
@Param("currentUserId") String currentUserId,
@Param("bureauDeptId") String bureauDeptId
);

2
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EfcodeCpReserveMapper.java

@ -21,7 +21,7 @@ public interface EfcodeCpReserveMapper extends BaseMapperPlus<EfcodeCpReserve, E
@Param("page") Page<EfcodeCpReserveVo> page,
@Param("resFrom") Long resFrom,
@Param("opStatus") Long opStatus,
@Param("currentUserId") Long currentUserId
@Param("currentUserId") String currentUserId
);
}

2
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpPlanServiceImpl.java

@ -120,7 +120,7 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService {
Long currentUserId = LoginHelper.getUserId();
Page<EfcodeCpPlanVo> page = pageQuery.build();
Page<EfcodeCpPlanVo> result = baseMapper.selectOtherPageList(page, bo, currentUserId, bureauDeptId);
Page<EfcodeCpPlanVo> result = baseMapper.selectOtherPageList(page, bo, String.valueOf(currentUserId), bureauDeptId);
// 后置处理:控制记录是否可预约
afterQueryOtherList(result.getRecords(), bureauDeptId, currentUserId);
return TableDataInfo.build(result);

2
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpReserveServiceImpl.java

@ -66,7 +66,7 @@ public class EfcodeCpReserveServiceImpl implements IEfcodeCpReserveService {
pageQuery.build(),
bo.getResFrom(),
bo.getOpStatus(),
bo.getCreateBy()
bo.getCreateBy() != null ? String.valueOf(bo.getCreateBy()) : null
);
return TableDataInfo.build(result);
}

8
zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeCpPlanMapper.xml

@ -22,6 +22,14 @@
AND is_delete = 0
<if test="currentUserId != null">
AND create_by != #{currentUserId}
<!-- 排除当前用户已预约的计划(活跃状态:1=同意, 3=待确认) -->
AND NOT EXISTS (
SELECT 1
FROM efcode_cp_reserve r
WHERE r.plan_group_id = efcode_cp_plan.id
AND r.sender_id = #{currentUserId}
AND r.op_status IN (1, 3)
)
</if>
<!-- 可见性:本单位全部 OR 其他单位公开 -->
<choose>

8
zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeCpReserveMapper.xml

@ -4,7 +4,7 @@
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zdxt.enforcementcode.mapper.EfcodeCpReserveMapper">
<!-- 预约处理列表(手动预约 / 系统匹配):关联计划表获取计划详情,按接收者ID过滤 -->
<!-- 预约处理列表(手动预约 / 系统匹配):关联计划表获取计划详情,查询当前用户收到的和发起的预约 -->
<select id="selectReservePageList" resultType="com.zdxt.enforcementcode.domain.vo.EfcodeCpReserveVo">
SELECT
r.id,
@ -19,6 +19,8 @@
r.op_set_time,
r.res_from,
r.warring_message,
r.sender_id,
r.receiver_id,
p.plan_date,
p.plan_time,
p.enterprise_name,
@ -38,9 +40,9 @@
<if test="opStatus != null">
AND r.op_status = #{opStatus}
</if>
<!-- 当前用户权限:仅查看以当前用户为接收者的预约记录 -->
<!-- 当前用户权限:查看当前用户收到的预约(别人预约我的) 或 发起的预约(我预约别人的) -->
<if test="currentUserId != null">
AND r.receiver_id = #{currentUserId}
AND (r.receiver_id = #{currentUserId} OR r.sender_id = #{currentUserId})
</if>
</where>
ORDER BY r.reserve_time DESC

9
zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/plan/cp_plan_other.vue

@ -354,17 +354,20 @@ const showCannotReserveDialog = (item, code, msg) => {
const onConfirmReserve = () => {
if (!currentReserveItem.value) return;
const reservedPlanId = currentReserveItem.value.id;
reserveSubmitting.value = true;
doReservePlan(
{ targetPlanId: currentReserveItem.value.id, opMessage: reserveMessage.value },
{ targetPlanId: reservedPlanId, opMessage: reserveMessage.value },
(res) => {
reserveSubmitting.value = false;
showReservePopup.value = false;
currentReserveItem.value = null;
if (res.code === 200) {
nextTick(() => { showSuccessToast('预约成功'); });
//
onRefresh();
//
planList.value = planList.value.filter(
item => String(item.id) !== String(reservedPlanId)
);
} else {
nextTick(() => { showDialog({ title: '提示', message: res.msg || '预约失败' }); });
}

26
zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_manual.vue

@ -29,7 +29,9 @@
@click="onSelectCard(item)"
>
<div class="card-header">
<div class="card-label"></div>
<div class="card-label" :class="{ 'sent-label': isSentByMe(item) }">
{{ isSentByMe(item) ? '我发起的' : '收到预约' }}
</div>
<div class="status" :class="getStatusClass(item.opStatus)">
{{ getStatusText(item.opStatus) }}
</div>
@ -67,8 +69,8 @@
<div class="value">{{ formatDate(item.reserveTime) }}</div>
</div>
<!-- 选中且待确认状态才显示操作按钮 -->
<div v-if="item.opStatus === 3 && selectedId === item.id" class="card-actions">
<!-- 选中且待确认状态且收到的预约才显示操作按钮我发起的不能操作 -->
<div v-if="item.opStatus === 3 && selectedId === item.id && !isSentByMe(item)" class="card-actions">
<div class="actions-left">
<div v-if="item.opMessage" class="action-item message-action" @click.stop="onShowMessage(item)">
<van-icon name="chat-o" />
@ -99,7 +101,9 @@
<script setup name="cpReserveManual">
import { showConfirmDialog, showSuccessToast, showDialog } from 'vant';
import { getReserveList, approveReserveByManual, rejectReserveByManual } from '@/api/enforcement/reserve';
import useCommon from '@/store/Common.js';
const commonStore = useCommon();
const selectedId = ref(null);
const reserveList = ref([]);
const loading = ref(false);
@ -159,9 +163,14 @@ const onLoad = () => {
fetchList();
};
//
const isSentByMe = (item) => {
return String(item.senderId) === String(commonStore.userId);
};
//
const onSelectCard = (item) => {
if (item.opStatus !== 3) return; //
if (item.opStatus !== 3 || isSentByMe(item)) return; //
selectedId.value = selectedId.value === item.id ? null : item.id;
};
@ -380,12 +389,17 @@ const afterOperate = (item, action) => {
.card-label {
font-size: 13px;
font-weight: 500;
color: #666;
background: #f0f6ff;
color: #1f75c9;
background: #e8f3ff;
padding: 3px 10px;
border-radius: 10px;
}
.card-label.sent-label {
color: #fa8c16;
background: #fff7e6;
}
.status {
font-size: 14px;
font-weight: 500;

32
zdxt-web-server/zdxt-admin-web-server/src/main/java/org/dromara/web/config/WebInterceptorConfig.java

@ -0,0 +1,32 @@
package org.dromara.web.config;
import org.dromara.web.interceptor.HttpRequestLoggingInterceptor;
import org.dromara.web.interceptor.UserAccessRestrictionInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Web拦截器配置
* <p>
* 注册自定义的 HTTP 请求日志拦截器和用户访问限制拦截器
* </p>
*
* @author zdxt
*/
@Configuration
public class WebInterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 全局HTTP请求日志
registry.addInterceptor(new HttpRequestLoggingInterceptor())
.addPathPatterns("/**")
.order(0);
// 用户访问限制:特定用户仅允许访问报表接口
registry.addInterceptor(new UserAccessRestrictionInterceptor())
.addPathPatterns("/**")
.order(1);
}
}

77
zdxt-web-server/zdxt-admin-web-server/src/main/java/org/dromara/web/interceptor/HttpRequestLoggingInterceptor.java

@ -0,0 +1,77 @@
package org.dromara.web.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* HTTP 请求日志拦截器
* <p>
* 拦截所有 HTTP 请求打印请求方法URI客户端IP请求头等关键信息
* 以及响应状态码和耗时便于调试和问题排查
* </p>
*
* @author zdxt
*/
@Slf4j
public class HttpRequestLoggingInterceptor implements HandlerInterceptor {
private static final ThreadLocal<Long> START_TIME = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
START_TIME.set(System.currentTimeMillis());
String method = request.getMethod();
String uri = request.getRequestURI();
String queryString = request.getQueryString();
String clientIp = getClientIp(request);
String userAgent = request.getHeader("User-Agent");
String contentType = request.getContentType();
log.info("[HTTP请求] {} {} | IP: {} | Content-Type: {} | User-Agent: {} | Query: {}",
method, uri, clientIp, contentType, userAgent, queryString);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// do nothing
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
Long startTime = START_TIME.get();
if (startTime != null) {
long duration = System.currentTimeMillis() - startTime;
int status = response.getStatus();
log.info("[HTTP响应] {} {} | 状态码: {} | 耗时: {}ms",
request.getMethod(), request.getRequestURI(), status, duration);
START_TIME.remove();
}
}
/**
* 获取客户端真实IP支持代理转发场景
*/
private String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// X-Forwarded-For 可能包含多个IP,取第一个
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
return ip;
}
}

102
zdxt-web-server/zdxt-admin-web-server/src/main/java/org/dromara/web/interceptor/UserAccessRestrictionInterceptor.java

@ -0,0 +1,102 @@
package org.dromara.web.interceptor;
import cn.dev33.satoken.stp.StpUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.HandlerInterceptor;
import java.io.PrintWriter;
import java.util.Objects;
/**
* 用户访问限制拦截器
* <p>
* 针对特定用户ID如17608642193仅允许访问报表相关接口
* /web/supervision/report/**其余接口一律拒绝访问
* </p>
*
* @author zdxt
*/
@Slf4j
public class UserAccessRestrictionInterceptor implements HandlerInterceptor {
/**
* 受限用户ID仅允许访问报表接口
*/
private static final Long RESTRICTED_USER_ID = 17608642193L;
/**
* 允许访问的路径关键字列表使用contains匹配兼容不同context-path前缀如/sup-ep
*/
private static final String[] ALLOWED_PATH_KEYWORDS = {
"/web/supervision/report/",
"/system/dict/data/type/",
"/web/enforcementcode/educationServices/",
"/web/supervision/carouselBanner/",
"/web/supervision/noticeInfo/",
"/web/supervision/noticeInfo/",
};
/**
* 额外允许访问的精确路径列表
*/
private static final String[] ALLOWED_EXACT_PATHS = {
"/sup-ep/system/user/getInfo",
"/sup-ep/system/menu/getRouters",
"/sup-ep/auth/tenant/list",
"/sup-ep/system/user/profile",
"/sup-ep/system/social/list",
"/sup-ep/monitor/online",
};
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 未登录的请求不在此拦截器处理(交给Sa-Token认证拦截器)
if (!LoginHelper.isLogin()) {
return true;
}
Long userId = LoginHelper.getUserId();
if (!Objects.equals(RESTRICTED_USER_ID, userId)) {
// 非受限用户,放行
return true;
}
String uri = request.getRequestURI();
// 检查允许的路径关键字
for (String keyword : ALLOWED_PATH_KEYWORDS) {
if (uri.contains(keyword)) {
log.debug("[访问限制] 用户ID={} 访问允许路径: {}, 允许", userId, uri);
return true;
}
}
// 检查额外允许的精确路径
for (String allowedPath : ALLOWED_EXACT_PATHS) {
if (uri.equals(allowedPath)) {
log.debug("[访问限制] 用户ID={} 访问白名单路径: {}, 允许", userId, uri);
return true;
}
}
// 受限用户访问非允许路径,拦截
log.warn("[访问限制] 用户ID={} 尝试访问非授权路径: {}, 已拒绝", userId, uri);
response.setStatus(HttpStatus.FORBIDDEN.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
R<Void> result = R.fail(HttpStatus.FORBIDDEN.value(), "当前用户仅允许访问报表相关接口");
PrintWriter writer = response.getWriter();
writer.write(OBJECT_MAPPER.writeValueAsString(result));
writer.flush();
return false;
}
}
Loading…
Cancel
Save