wangmingyong 4 months ago
parent
commit
b39d4a93dc
  1. 1
      ng/3/nginx.conf
  2. 10
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enterprise/AppEntLawEnforcementCaseController.java
  3. 2
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EfcodeCpPlanMapper.java
  4. 2
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EfcodeCpReserveMapper.java
  5. 2
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpPlanServiceImpl.java
  6. 2
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeCpReserveServiceImpl.java
  7. 2
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EnforcementRegistrationServiceImpl.java
  8. 8
      zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeCpPlanMapper.xml
  9. 8
      zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeCpReserveMapper.xml
  10. 2
      zdxt-modules/zdxt-enforcement-nos/src/main/java/com/zdxt/enforcementnos/service/impl/NosLawEnforcementTaskServiceImpl.java
  11. 9
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/plan/cp_plan_other.vue
  12. 26
      zdxt-web-client/zdxt-efcode-enforcement-app/src/views/enforcement/my/reserve/cp_reserve_manual.vue
  13. 5
      zdxt-web-client/zdxt-efcode-enterprise-app/src/http/http.js
  14. 22
      zdxt-web-client/zdxt-efcode-enterprise-app/src/util/fileUpload.js
  15. 2
      zdxt-web-client/zdxt-efcode-enterprise-app/src/views/enterprise/nos/components/NosDetailMaterialsTab.vue
  16. 32
      zdxt-web-server/zdxt-admin-web-server/src/main/java/org/dromara/web/config/WebInterceptorConfig.java
  17. 77
      zdxt-web-server/zdxt-admin-web-server/src/main/java/org/dromara/web/interceptor/HttpRequestLoggingInterceptor.java
  18. 102
      zdxt-web-server/zdxt-admin-web-server/src/main/java/org/dromara/web/interceptor/UserAccessRestrictionInterceptor.java

1
ng/3/nginx.conf

@ -19,6 +19,7 @@ http {
default_type application/octet-stream;
add_header X-Frame-Options SAMEORIGIN;
server_tokens off;
client_max_body_size 50M;
# 定义后端集群
upstream java_cluster {

10
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/app/enterprise/AppEntLawEnforcementCaseController.java

@ -49,8 +49,8 @@ public class AppEntLawEnforcementCaseController extends BaseController {
*/
@ApiResponse(description = "企业端-执法案件信息详情")
@GetMapping("/lawCasedetail")
public AppLawEnforcementCaseVo lawCasedetail(AppLawEnforcementCaseBo bo) {
return appLawEnforcementCaseService.selectAppLawEnforcementCaseById(bo);
public R<AppLawEnforcementCaseVo> lawCasedetail(AppLawEnforcementCaseBo bo) {
return R.ok(appLawEnforcementCaseService.selectAppLawEnforcementCaseById(bo));
}
/**
@ -60,9 +60,9 @@ public class AppEntLawEnforcementCaseController extends BaseController {
*/
@ApiResponse(description = "企业端-执法信息详情")
@GetMapping("/lawDetail")
public AppEnforcementRegistrationVo lawDetail(AppLawEnforcementCaseBo bo) {
return null;
// return appLawEnforcementCaseService.selectAppLawEnforcementLawDetailById(bo);
public R<AppEnforcementRegistrationVo> lawDetail(AppLawEnforcementCaseBo bo) {
AppEnforcementRegistrationVo result = appLawEnforcementCaseService.selectAppLawEnforcementLawDetailById(bo);
return R.ok("获取成功", result);
}
}

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);
}

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

@ -1057,7 +1057,7 @@ public class EnforcementRegistrationServiceImpl implements IEnforcementRegistrat
for (Map.Entry<String, List<EnforcementRegistration>> entry : groupedByNo.entrySet()) {
Map<String, Object> map = new HashMap<>();
for (EnforcementRegistration e : entry.getValue()) {
map.put(e.getId(), e);
map.put(null, e);
}
result.add(map);
}

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

2
zdxt-modules/zdxt-enforcement-nos/src/main/java/com/zdxt/enforcementnos/service/impl/NosLawEnforcementTaskServiceImpl.java

@ -398,7 +398,7 @@ public class NosLawEnforcementTaskServiceImpl extends ServiceImpl<NosLawEnforcem
@Transactional(rollbackFor = Exception.class)
public Boolean approveTask(NosLawEnforcementTaskApproveBO bo) {
Integer platformType = LoginHelper.getPlatformType();
if(!PlatformTypeEnums.ENFORCEMENT.getKey().equals(platformType)) {
if(!PlatformTypeEnums.ENFORCEMENT.getKey().equals(platformType.longValue())) {
throw new ServiceException("当前用户不是执法端用户,无法对任务进行审核");
}

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;

5
zdxt-web-client/zdxt-efcode-enterprise-app/src/http/http.js

@ -17,7 +17,10 @@ const router = $globalRouter;
//请求拦截器添加head
http.interceptors.request.use(
config => {
if(!config.headers['Content-Type']){
// FormData 必须由浏览器/axios 自动设置 multipart boundary
if (config.data instanceof FormData) {
delete config.headers['Content-Type'];
} else if(!config.headers['Content-Type']){
config.headers['Content-Type'] = 'application/json'
}
const token = $globalStore.useCommon.token;

22
zdxt-web-client/zdxt-efcode-enterprise-app/src/util/fileUpload.js

@ -1,25 +1,12 @@
import {_form, _post} from '../http/index'
import { baseUrl } from "../config/env";
import axios from 'axios';
const http = axios.create({
traditional: true,
contentType : "application/json",
timeout: 1000 * 500000, // 1 秒
});
const uploadFile = (files,callback) => {
import http from '../http/http';
const uploadFile = (files, callback) => {
files.status = "uploading";
files.message = "上传中...";
// 需要使用 FormData 进行文件上传
let form = new FormData();
form.append("file", files.file);
axios({
method: "post",
url: baseUrl+ "/app-enf/common/file/upload",
data: form,
headers: {
'Content-Type': 'multipart/form-data'
}
})
http.post("/app-enf/common/file/upload", form)
.then((res) => {
files.status = "done";
files.message = "上传成功";
@ -30,7 +17,6 @@ const uploadFile = (files,callback) => {
files.message = "上传失败";
console.log(err);
});
}
const nameCorrect=(name)=>{

2
zdxt-web-client/zdxt-efcode-enterprise-app/src/views/enterprise/nos/components/NosDetailMaterialsTab.vue

@ -341,7 +341,7 @@ const onAfterReadImage = (file) => {
files.forEach((f) => {
uploadFile(f, (res) => {
const data = res && res.data ? res.data : res
const filePath = typeof data === 'string' ? data : (data && (data.url || data.fileUrl || data.path)) || ''
const filePath = typeof data === 'string' ? data : (data && (data.data || data.url || data.fileUrl || data.path)) || ''
if (filePath) {
f.url = toFullFileUrl(filePath)
if (isVideo(filePath) || isPdf(filePath)) {

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