Merge remote-tracking branch 'origin/main'

This commit is contained in:
wangyilin 2024-11-21 15:16:31 +08:00
commit 92b120380e
42 changed files with 674 additions and 350 deletions

View File

@ -1,7 +1,7 @@
# 配置文档参考 https://taro-docs.jd.com/docs/next/env-mode-config
TARO_APP_ID="wx0acd1c4fcf94bdd3"
# TARO_APP_BASE_API="http://172.10.10.93:8765"
TARO_APP_BASE_API="https://www.hnjinglian.cn:5678"
TARO_APP_BASE_API="http://172.10.10.93:8765"
# TARO_APP_BASE_API="https://www.hnjinglian.cn:5678"

View File

@ -1,17 +0,0 @@
package com.changhu.common.annotation;
import com.changhu.common.enums.OpenApiType;
import java.lang.annotation.*;
/**
* @author 20252
* @createTime 2024/10/9 下午5:14
* @desc 检查openApi
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckOpenApi {
OpenApiType value();
}

View File

@ -1,22 +0,0 @@
package com.changhu.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.List;
/**
* @author 20252
* @createTime 2024/10/9 下午5:10
* @desc OpenApiType...
*/
@Getter
@AllArgsConstructor
public enum OpenApiType {
Information_on_enterprises_and_institutions("获取企事业单位信息", Arrays.asList("1fe0aaf3-45a4-4be3-a989-75e914a3f36e", "1fe0aaf3-45a4-a989-75e914a3f36e")),
data_view("数据总览", List.of("8da74bbf-c686-4393-b4ec-692091e6d381"));
private final String desc;
private final List<String> openApiKeys;
}

View File

@ -0,0 +1,22 @@
package com.changhu.common.pojo.params;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.locationtech.jts.geom.Point;
/**
* @author 20252
* @createTime 2024/11/21 上午11:18
* @desc MapVisionParams...
*/
@Data
public class MapVisionParams {
@Schema(description = "左上角")
private Point upperLeft;
@Schema(description = "右上角")
private Point upperRight;
@Schema(description = "左下角")
private Point lowerLeft;
@Schema(description = "右下角")
private Point lowerRight;
}

View File

@ -2,10 +2,13 @@ package com.changhu.config;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.stp.StpUtil;
import com.changhu.support.filter.BodyWrapperFilter;
import com.changhu.support.interceptor.JsonBodyInterceptor;
import com.changhu.support.interceptor.OpenApiInterceptor;
import com.changhu.support.interceptor.SignInterceptor;
import com.changhu.support.interceptor.UserTypeInterceptor;
import org.jetbrains.annotations.NotNull;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
@ -59,10 +62,19 @@ public class WebConfig implements WebMvcConfigurer {
// 注册clientType 拦截器 用于校验当前用户是否匹配操作客户端
registry.addInterceptor(new UserTypeInterceptor());
// 注册开放接口 拦截器 用于校验第三方是否携带指定apiKey
registry.addInterceptor(new OpenApiInterceptor())
registry.addInterceptor(new SignInterceptor())
.addPathPatterns("/open/**");
}
@Bean
public FilterRegistrationBean<BodyWrapperFilter> loggingFilter() {
FilterRegistrationBean<BodyWrapperFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new BodyWrapperFilter());
registrationBean.addUrlPatterns("/open/*"); // 指定过滤的URL模式
registrationBean.setOrder(1000);
return registrationBean;
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")

View File

@ -2,7 +2,6 @@ package com.changhu.controller;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.lang.Dict;
import com.alibaba.fastjson2.JSON;
import com.changhu.common.annotation.CheckUserType;
import com.changhu.common.annotation.JsonBody;
import com.changhu.common.db.enums.UserType;
@ -67,23 +66,4 @@ public class AccessKeysController {
return accessKeysService.list();
}
public static void main(String[] args) {
Class<OpenController> openControllerClass = OpenController.class;
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(openControllerClass, RequestMapping.class);
String controllerPath = requestMapping.value()[0];
List<SelectNodeVo<String>> method = Arrays.stream(openControllerClass.getMethods())
.filter(m -> AnnotationUtil.hasAnnotation(m, RequestMapping.class))
.map(m -> {
Operation operation = m.getAnnotation(Operation.class);
RequestMapping mReq = AnnotatedElementUtils.findMergedAnnotation(m, RequestMapping.class);
return SelectNodeVo.<String>builder()
.value(controllerPath + mReq.value()[0])
.label(operation.summary())
.extData(Dict.of("method", mReq.method()))
.build();
})
.toList();
method.forEach(i -> System.out.println(JSON.toJSONString(i)));
}
}

View File

@ -1,13 +1,12 @@
package com.changhu.controller;
import com.changhu.common.annotation.CheckOpenApi;
import com.changhu.common.annotation.JsonBody;
import com.changhu.common.enums.OpenApiType;
import com.changhu.common.pojo.vo.SelectNodeVo;
import com.changhu.pojo.dto.DataViewDTO;
import com.changhu.pojo.dto.EnterprisesUnitDetailDTO;
import com.changhu.pojo.dto.SecurityUnitUseStatisticsDTO;
import com.changhu.pojo.dto.ServiceProjectSecurityUserRosterDTO;
import com.changhu.pojo.dto.SecurityUserRosterDTO;
import com.changhu.pojo.params.EnterprisesUnitOrServiceProjectType;
import com.changhu.service.OpenApiService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
@ -33,7 +32,6 @@ public class OpenController {
private OpenApiService openApiService;
@Operation(summary = "获取企事业单位列表")
@CheckOpenApi(value = OpenApiType.Information_on_enterprises_and_institutions)
@GetMapping("/getEnterprisesUnit")
public List<SelectNodeVo<Long>> getEnterprisesUnit(@Schema(description = "代码") @RequestParam String code,
@Schema(description = "等级") @RequestParam Integer level) {
@ -41,31 +39,29 @@ public class OpenController {
}
@Operation(summary = "企事业单位详情")
@CheckOpenApi(value = OpenApiType.Information_on_enterprises_and_institutions)
@GetMapping("/enterprisesUnitDetailById")
public EnterprisesUnitDetailDTO enterprisesUnitDetailById(@Schema(description = "企事业单位id") @RequestParam Long enterprisesUnitId) {
return openApiService.enterprisesUnitDetailById(enterprisesUnitId);
}
@Operation(summary = "数据总览")
@CheckOpenApi(value = OpenApiType.data_view)
@GetMapping("/dataView")
public DataViewDTO dataView() {
return openApiService.dataView();
}
@Operation(summary = "保安单位使用情况统计")
@CheckOpenApi(value = OpenApiType.Information_on_enterprises_and_institutions)
@GetMapping("/securityUnitUseStatistics")
public List<SecurityUnitUseStatisticsDTO> securityUnitUseStatistics(@Schema(description = "代码") @RequestParam String code,
@Schema(description = "等级") @RequestParam Integer level) {
return openApiService.securityUnitUseStatistics(code, level);
}
@Operation(summary = "服务项目安保人员花名册")
@CheckOpenApi(value = OpenApiType.Information_on_enterprises_and_institutions)
@GetMapping("/serviceProjectUserRoster")
public List<ServiceProjectSecurityUserRosterDTO> serviceProjectUserRoster(@Schema(description = "服务项目id") Long serviceProjectId) {
return openApiService.serviceProjectUserRoster(serviceProjectId);
@Operation(summary = "安保人员花名册")
@GetMapping("/securityUserRoster")
public List<SecurityUserRosterDTO> securityUserRoster(@Schema(description = "事业单位或服务项目id") Long id,
@Schema(description = "类型") EnterprisesUnitOrServiceProjectType type) {
return openApiService.securityUserRoster(id, type);
}
}

View File

@ -2,7 +2,8 @@ package com.changhu.mapper;
import com.changhu.common.pojo.vo.SelectNodeVo;
import com.changhu.pojo.dto.SecurityUnitUseStatisticsDTO;
import com.changhu.pojo.dto.ServiceProjectSecurityUserRosterDTO;
import com.changhu.pojo.dto.SecurityUserRosterDTO;
import com.changhu.pojo.params.EnterprisesUnitOrServiceProjectType;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@ -38,9 +39,7 @@ public interface OpenApiMapper {
/**
* 获取服务人员花名册
*
* @param serviceProjectId 服务项目id
* @return 结果
*/
List<ServiceProjectSecurityUserRosterDTO> serviceProjectUserRoster(@Param("serviceProjectId") Long serviceProjectId);
List<SecurityUserRosterDTO> securityUserRoster(@Param("id") Long id,
@Param("type") EnterprisesUnitOrServiceProjectType type);
}

View File

@ -6,7 +6,6 @@ import com.changhu.common.annotation.JsonBody;
import com.changhu.common.db.enums.UserType;
import com.changhu.common.exception.MessageException;
import com.changhu.common.pojo.vo.SelectNodeVo;
import com.changhu.module.management.pojo.entity.EnterprisesUnit;
import com.changhu.module.management.pojo.params.EnterprisesUnitSaveOrUpdateParams;
import com.changhu.module.management.pojo.queryParams.EnterprisesUnitPagerQueryParams;
import com.changhu.module.management.pojo.vo.EnterprisesUnitPagerVo;
@ -64,10 +63,4 @@ public class EnterprisesUnitController {
return enterprisesUnitService.queryListByAdministrativeDivisionCodes(administrativeDivisionCodes);
}
@Operation(summary = "企事业单位地图点位")
@GetMapping("/mapPoint")
public List<EnterprisesUnit> mapPoint() {
return enterprisesUnitService.list();
}
}

View File

@ -0,0 +1,34 @@
package com.changhu.module.superManagement.controller;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.changhu.common.annotation.JsonBody;
import com.changhu.module.management.pojo.entity.EnterprisesUnit;
import com.changhu.module.superManagement.service.SuperIndexService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
/**
* @author 20252
* @createTime 2024/11/21 上午11:31
* @desc SuperIndexController...
*/
@JsonBody
@Tag(name = "超级后台首页")
@RequestMapping("/super/index")
public class SuperIndexController {
@Autowired
private SuperIndexService superIndexService;
@Operation(summary = "企事业单位地图点位")
@GetMapping("/mapPoint")
public List<EnterprisesUnit> mapPoint() {
return Db.lambdaQuery(EnterprisesUnit.class).list();
}
}

View File

@ -0,0 +1,12 @@
package com.changhu.module.superManagement.mapper;
import org.apache.ibatis.annotations.Mapper;
/**
* @author 20252
* @createTime 2024/11/21 上午11:32
* @desc SuperIndexMapper...
*/
@Mapper
public interface SuperIndexMapper {
}

View File

@ -0,0 +1,9 @@
package com.changhu.module.superManagement.service;
/**
* @author 20252
* @createTime 2024/11/21 上午11:31
* @desc SuperIndexService...
*/
public interface SuperIndexService {
}

View File

@ -0,0 +1,13 @@
package com.changhu.module.superManagement.service.impl;
import com.changhu.module.superManagement.service.SuperIndexService;
import org.springframework.stereotype.Service;
/**
* @author 20252
* @createTime 2024/11/21 上午11:31
* @desc SuperIndexServiceImpl...
*/
@Service
public class SuperIndexServiceImpl implements SuperIndexService {
}

View File

@ -7,6 +7,8 @@ import com.changhu.module.management.pojo.model.LegalPersonInfo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* @author 20252
* @createTime 2024/11/18 上午10:32
@ -14,31 +16,63 @@ import lombok.Data;
*/
@Data
public class SecurityUnitUseStatisticsDTO {
@Schema(description = "服务项目id")
private Long serviceProjectId;
@Schema(description = "事业单位id")
private Long enterprisesUnitId;
@Schema(description = "事业单位名称")
private String enterprisesUnitName;
@Schema(description = "省编码")
private String province;
@Schema(description = "")
private String provinceName;
@Schema(description = "市编码")
private String city;
@Schema(description = "")
private String cityName;
@Schema(description = "区/县编码")
private String districts;
@Schema(description = "区/县")
private String districtsName;
@Schema(description = "街道编码")
private String street;
@Schema(description = "街道")
private String streetName;
@Schema(description = "事业单位详细地址")
private String address;
@Schema(description = "服务项目列表")
List<_ServiceProjectVo> serviceProjectList;
@Schema(description = "公安单位id")
private Long policeUnitId;
@Schema(description = "公安单位名称")
private String policeUnitName;
@Schema(description = "保安单位名称")
private String securityUnitName;
@Data
static class _ServiceProjectVo {
@Schema(description = "服务项目id")
private Long snowFlakeId;
@Schema(description = "服务项目名称")
private String serviceProjectName;
private String name;
@Schema(description = "保安服务类别")
private ServiceProjectType type;
@Schema(description = "二级类型")
private ServiceProjectTwoType twoType;
@Schema(description = "外包公司名称")
private String outsourceName;
@Schema(description = "是否备案")
private IsOrNot isFiling;
@Schema(description = "证件号(保安服务许可证/备案证)")
private String idNumber;
@Schema(description = "保安人员总数")
private Integer securityUserTotal;
@Schema(description = "持证保安数量")
private Integer haveCardSecurityUserCount;
@Schema(description = "是否备案")
private IsOrNot isFiling;
@Schema(description = "证件号(保安服务许可证/备案证)")
private String idNumber;
@Schema(description = "保安单位id")
private Long securityUnitId;
@Schema(description = "保安单位名称")
private String securityUnitName;
@Schema(description = "保安单位法人信息")
private LegalPersonInfo securityUnitLegalPersonInfo;
@ -48,3 +82,5 @@ public class SecurityUnitUseStatisticsDTO {
private String serviceProjectManagerTelephone;
}
}

View File

@ -9,10 +9,10 @@ import lombok.Data;
/**
* @author 20252
* @createTime 2024/11/18 上午10:43
* @desc ServiceProjectSecurityUserRosterDTO...
* @desc SecurityUserRosterDTO...
*/
@Data
public class ServiceProjectSecurityUserRosterDTO {
public class SecurityUserRosterDTO {
@Schema(description = "id")
private Long snowFlakeId;
@Schema(description = "名称")

View File

@ -0,0 +1,16 @@
package com.changhu.pojo.params;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author 20252
* @createTime 2024/11/21 上午10:35
* @desc EnterprisesUnitOrServiceProjectType...
*/
@Getter
@AllArgsConstructor
public enum EnterprisesUnitOrServiceProjectType {
ENTERPRISES_UNIT,
SERVICE_PROJECT
}

View File

@ -4,7 +4,8 @@ import com.changhu.common.pojo.vo.SelectNodeVo;
import com.changhu.pojo.dto.DataViewDTO;
import com.changhu.pojo.dto.EnterprisesUnitDetailDTO;
import com.changhu.pojo.dto.SecurityUnitUseStatisticsDTO;
import com.changhu.pojo.dto.ServiceProjectSecurityUserRosterDTO;
import com.changhu.pojo.dto.SecurityUserRosterDTO;
import com.changhu.pojo.params.EnterprisesUnitOrServiceProjectType;
import java.util.List;
@ -50,8 +51,9 @@ public interface OpenApiService {
/**
* 服务项目安保人员花名册
*
* @param serviceProjectId 服务项目id
* @param id 事业单位服务项目id
* @param type 类型
* @return 花名册
*/
List<ServiceProjectSecurityUserRosterDTO> serviceProjectUserRoster(Long serviceProjectId);
List<SecurityUserRosterDTO> securityUserRoster(Long id, EnterprisesUnitOrServiceProjectType type);
}

View File

@ -12,7 +12,8 @@ import com.changhu.module.miniProgram.pojo.entity.SecurityUser;
import com.changhu.pojo.dto.DataViewDTO;
import com.changhu.pojo.dto.EnterprisesUnitDetailDTO;
import com.changhu.pojo.dto.SecurityUnitUseStatisticsDTO;
import com.changhu.pojo.dto.ServiceProjectSecurityUserRosterDTO;
import com.changhu.pojo.dto.SecurityUserRosterDTO;
import com.changhu.pojo.params.EnterprisesUnitOrServiceProjectType;
import com.changhu.service.OpenApiService;
import com.changhu.support.mybatisplus.pojo.entity.BaseEntity;
import lombok.SneakyThrows;
@ -103,7 +104,7 @@ public class OpenApiServiceImpl implements OpenApiService {
}
@Override
public List<ServiceProjectSecurityUserRosterDTO> serviceProjectUserRoster(Long serviceProjectId) {
return openApiMapper.serviceProjectUserRoster(serviceProjectId);
public List<SecurityUserRosterDTO> securityUserRoster(Long id, EnterprisesUnitOrServiceProjectType type) {
return openApiMapper.securityUserRoster(id, type);
}
}

View File

@ -86,7 +86,8 @@ public class FastJson2Config {
//4.解决中文乱码问题相当于在Controller上的@RequestMapping中加了个属性produces = "application/json"
fastConverter.setSupportedMediaTypes(List.of(
MediaType.APPLICATION_JSON
MediaType.APPLICATION_JSON,
MediaType.APPLICATION_FORM_URLENCODED
));
return fastConverter;
}

View File

@ -0,0 +1,27 @@
package com.changhu.support.filter;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.util.Objects;
/**
* @author 20252
* @createTime 2024/11/19 下午3:07
* @desc BodyWrapperFilter...
*/
@Slf4j
public class BodyWrapperFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("进入filter:{}", servletRequest.getRemoteAddr());
ServletRequest requestWrapper = null;
if (servletRequest instanceof HttpServletRequest) {
requestWrapper = new CustomHttpServletRequestWrapper((HttpServletRequest) servletRequest);
}
filterChain.doFilter(Objects.requireNonNullElse(requestWrapper, servletRequest), servletResponse);
}
}

View File

@ -0,0 +1,65 @@
package com.changhu.support.filter;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* @author 20252
* @createTime 2024/11/19 下午3:12
* @desc CustomHttpServletRequestWrapper...
*/
public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public CustomHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
BufferedReader reader = request.getReader();
try (StringWriter writer = new StringWriter()) {
int read;
char[] buf = new char[1024 * 8];
while ((read = reader.read(buf)) != -1) {
writer.write(buf, 0, read);
}
this.body = writer.getBuffer().toString().getBytes();
}
}
public String getBody() {
return new String(body, StandardCharsets.UTF_8);
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() {
return byteArrayInputStream.read();
}
};
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}

View File

@ -1,38 +0,0 @@
package com.changhu.support.interceptor;
import com.changhu.common.annotation.CheckOpenApi;
import com.changhu.common.exception.MessageException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author 20252
* @createTime 2024/10/9 下午5:05
* @desc OpenApiInterceptor...
*/
@Slf4j
public class OpenApiInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) {
String header = request.getHeader("X-API-KEY");
log.info("apiKey:{} {} 请求:{}", header, LocalDateTime.now(), request.getRequestURI());
if (handler instanceof HandlerMethod handlerMethod) {
CheckOpenApi methodAnnotation = handlerMethod.getMethodAnnotation(CheckOpenApi.class);
if (methodAnnotation != null) {
List<String> openApiKeys = methodAnnotation.value().getOpenApiKeys();
if (!openApiKeys.contains(header)) {
throw new MessageException("openApiKey error!");
}
}
}
return true;
}
}

View File

@ -0,0 +1,118 @@
package com.changhu.support.interceptor;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.changhu.common.db.enums.IsEnable;
import com.changhu.common.exception.MessageException;
import com.changhu.common.utils.IpUtil;
import com.changhu.pojo.entity.AccessKeys;
import com.changhu.support.filter.CustomHttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author 20252
* @createTime 2024/11/19 下午1:58
* @desc SignInterceptor...
*/
@Slf4j
public class SignInterceptor implements HandlerInterceptor {
private static final String ACCESS_KEY = "Access-Key";//调用者身份唯一标识
private static final String TIMESTAMP = "Time-Stamp";//时间戳
private static final String SIGN = "Sign";//签名
@Override
public boolean preHandle(@NotNull HttpServletRequest request,
@NotNull HttpServletResponse response,
@NotNull Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
String ip = IpUtil.getIp(request);
try {
return checkSign(request);
} catch (MessageException e) {
log.error("开放接口访问失败:{} 访问时间:{} IP:{} 访问接口:{} ", e.getMessage(), LocalDateTime.now(), ip, request.getRequestURI());
throw e;
}
}
return false;
}
private boolean checkSign(HttpServletRequest request) throws MessageException {
String accessKey = request.getHeader(ACCESS_KEY);
String timestamp = request.getHeader(TIMESTAMP);
String sign = request.getHeader(SIGN);
if (StrUtil.isBlank(accessKey) || StrUtil.isBlank(timestamp) || StrUtil.isBlank(sign)) {
throw new MessageException("请求体缺失");
}
AccessKeys accessKeyEntity = Db.lambdaQuery(AccessKeys.class)
.eq(AccessKeys::getAccessKey, accessKey)
.oneOpt()
.orElseThrow(() -> new MessageException("无效的accessKey"));
if (IsEnable.FALSE.equals(accessKeyEntity.getIsEnable())) {
throw new MessageException("accessKey已禁用");
}
if (accessKeyEntity.getEffectiveTime() > 0 && System.currentTimeMillis() - Long.parseLong(timestamp) > accessKeyEntity.getEffectiveTime()) {
throw new MessageException("请求已过期");
}
List<String> allowedResources = Optional.ofNullable(accessKeyEntity.getAllowedResources()).orElseThrow(() -> new MessageException("暂无允许访问的资源"));
if (!allowedResources.contains(request.getRequestURI())) {
throw new MessageException("无效的请求资源");
}
Map<String, Object> hashMap = new HashMap<>();
hashMap.put(ACCESS_KEY, accessKey);
hashMap.put(TIMESTAMP, timestamp);
//添加请求url参数
Optional.ofNullable(HttpUtil.decodeParamMap(request.getQueryString(), StandardCharsets.UTF_8))
.ifPresent(hashMap::putAll);
//添加body参数
Optional.ofNullable(JSON.<Map<String, Object>>parseObject(((CustomHttpServletRequestWrapper) request).getBody(), Map.class))
.ifPresent(hashMap::putAll);
//生成签名
String nowSign = generatedSign(hashMap, accessKeyEntity.getSecretKey());
//比对签名
if (!sign.equals(nowSign)) {
throw new MessageException("签名错误");
}
return true;
}
/**
* 获取签名
*
* @param map 参数结果
* @param secretKey 密钥
* @return 签名字符串
*/
private String generatedSign(Map<String, Object> map, String secretKey) {
List<Map.Entry<String, Object>> entries = new ArrayList<>(map.entrySet());
String str = entries.stream()
.filter(en -> null == en.getValue() || StrUtil.isNotBlank(en.getValue().toString()))
.sorted(Comparator.comparing(o -> o.getKey().toLowerCase()))
.map(en -> StrUtil.format("{}={}", en.getKey(), URLUtil.encodeAll(en.getValue() + "")))
.collect(Collectors.joining("&"));
str += ("&Secret-Key=" + secretKey);
return MD5.create().digestHex(str).toUpperCase();
}
}

View File

@ -14,8 +14,8 @@
from ck_assessment_record car
left join enterprises_unit eu on car.enterprises_unit_id = eu.snow_flake_id and eu.delete_flag = 0
left join ck_project cp on car.ck_project_id = cp.snow_flake_id and cp.delete_flag = 0
left join mini_program_user mpu on mpu.identity = 'police' and car.create_by = mpu.snow_flake_id and mpu.delete_flag = 0
left join police_unit pu on mpu.unit_id = pu.snow_flake_id and pu.delete_flag = 0
left join mini_program_user mpu on mpu.identity = 'police' and car.create_by = mpu.snow_flake_id
left join police_unit pu on mpu.unit_id = pu.snow_flake_id
left join ck_assessment_record_details card on car.snow_flake_id = card.ck_assessment_record_id and card.delete_flag = 0
left join ck_standard cs on card.ck_standard_id = cs.snow_flake_id
where car.delete_flag = 0

View File

@ -30,33 +30,64 @@
</select>
<resultMap id="SecurityUnitUseStatisticsDTOResultMap" type="com.changhu.pojo.dto.SecurityUnitUseStatisticsDTO">
<result
column="securityUnitLegalPersonInfo"
column="serviceProjectList"
typeHandler="com.baomidou.mybatisplus.extension.handlers.Fastjson2TypeHandler"
property="securityUnitLegalPersonInfo"/>
property="serviceProjectList"/>
</resultMap>
<select id="securityUnitUseStatistics" resultMap="SecurityUnitUseStatisticsDTOResultMap">
WITH security_user_counts AS (
SELECT
service_project_id,
COUNT(1) AS securityUserTotal,
SUM(IF(security_number != '', 1, 0)) AS haveCardSecurityUserCount
FROM
security_user
WHERE delete_flag = 0
GROUP BY service_project_id )
select
sp.snow_flake_id as 'serviceProjectId',
eu.snow_flake_id as 'enterprisesUnitId',
eu.name as 'enterprisesUnitName',
eu.province,
eu.city,
eu.districts,
eu.street,
ad1.name as 'provinceName',
ad2.name as 'cityName',
ad3.name as 'districtsName',
ad4.name as 'streetName',
eu.address as 'address',
pu.snow_flake_id as 'policeUnitId',
pu.name as 'policeUnitName',
su.name as 'securityUnitName',
sp.name as 'serviceProjectName',
sp.type as 'type',
sp.two_type as 'twoType',
sp.outsource_name as 'outsourceName',
count(suu.snow_flake_id) as 'securityUserTotal',
SUM(IF(suu.security_number != '', 1, 0)) as 'haveCardSecurityUserCount',
sp.is_filing as 'isFiling',
sp.id_number,
su.legal_person_info as 'securityUnitLegalPersonInfo',
mpu.name as 'serviceProjectManagerName',
mpu.telephone as 'serviceProjectManagerTelephone'
from police_unit pu
json_arrayagg(
json_object(
'snowFlakeId', sp.snow_flake_id,
'name', sp.name,
'type', sp.type,
'twoType', sp.two_type,
'outsourceName', sp.outsource_name,
'isFiling', sp.is_filing,
'idNumber', sp.id_number,
'securityUserTotal', suc.securityUserTotal,
'haveCardSecurityUserCount', suc.haveCardSecurityUserCount,
'securityUnitId',su.snow_flake_id,
'securityUnitName',su.name,
'securityUnitLegalPersonInfo',su.legal_person_info,
'serviceProjectManagerName',mpu.name,
'serviceProjectManagerTelephone',mpu.telephone
)) as 'serviceProjectList'
from
police_unit pu
join enterprises_unit eu on pu.snow_flake_id = eu.police_unit_id and eu.delete_flag = 0
join service_project sp on eu.snow_flake_id = sp.enterprises_unit_id and sp.delete_flag = 0
join security_unit su on sp.security_unit_id = su.snow_flake_id and su.delete_flag = 0
LEFT join mini_program_user mpu on sp.project_manager_mini_program_user_id = mpu.snow_flake_id
LEFT join security_user suu on suu.service_project_id = sp.snow_flake_id and suu.delete_flag = 0
where pu.delete_flag = 0
left join administrative_division ad1 on eu.province = ad1.code and ad1.delete_flag = 0
left join administrative_division ad2 on eu.city = ad2.code and ad2.delete_flag = 0
left join administrative_division ad3 on eu.districts = ad3.code and ad3.delete_flag = 0
left join administrative_division ad4 on eu.street = ad4.code and ad4.delete_flag = 0
left JOIN security_user_counts suc ON sp.snow_flake_id = suc.service_project_id
where
pu.delete_flag = 0
<choose>
<when test="level==1">
and eu.province = #{code}
@ -75,11 +106,11 @@
</when>
<otherwise>and eu.snow_flake_id = -1</otherwise>
</choose>
group by sp.snow_flake_id
order by sp.create_time desc
group by eu.snow_flake_id
order by eu.create_time desc
</select>
<select id="serviceProjectUserRoster"
resultType="com.changhu.pojo.dto.ServiceProjectSecurityUserRosterDTO">
<select id="securityUserRoster"
resultType="com.changhu.pojo.dto.SecurityUserRosterDTO">
select su.snow_flake_id,
su.name,
su.sex,
@ -90,11 +121,21 @@
suu.name as 'securityUnitName',
su.security_number
from enterprises_unit eu
left join service_project sp on sp.enterprises_unit_id = eu.snow_flake_id and sp.delete_flag = 0
left join security_user su on su.service_project_id = sp.snow_flake_id and su.delete_flag = 0
left join security_unit suu on su.security_unit_id = suu.snow_flake_id and suu.delete_flag = 0
LEFT JOIN service_project sp ON sp.enterprises_unit_id = eu.snow_flake_id AND sp.delete_flag = 0
JOIN security_user su ON su.service_project_id = sp.snow_flake_id AND su.delete_flag = 0
LEFT JOIN security_unit suu ON su.security_unit_id = suu.snow_flake_id AND suu.delete_flag = 0
where eu.delete_flag = 0
and sp.snow_flake_id = #{serviceProjectId}
<choose>
<when test="type==@com.changhu.pojo.params.EnterprisesUnitOrServiceProjectType@ENTERPRISES_UNIT">
and eu.snow_flake_id = #{id}
</when>
<when test="type==@com.changhu.pojo.params.EnterprisesUnitOrServiceProjectType@SERVICE_PROJECT">
and sp.snow_flake_id = #{id}
</when>
<otherwise>
and eu.snow_flake_id = -1
</otherwise>
</choose>
order by su.create_time desc
</select>
</mapper>

View File

@ -1,8 +1,7 @@
VITE_APP_NAME=超级后台
VITE_APP_NAME=保安管理
VITE_APP_ENV=development
VITE_APP_PORT=1000
VITE_DROP_CONSOLE=false
VITE_APP_MODULE_NAME=superManagement
# axios
VITE_APP_BASE_API=/api

View File

@ -1,8 +1,7 @@
VITE_APP_NAME=超级后台
VITE_APP_NAME=保安管理
VITE_APP_ENV=production
VITE_APP_PORT=1001
VITE_DROP_CONSOLE=true
VITE_APP_MODULE_NAME=superManagement
# axios
VITE_APP_BASE_API=/api

View File

@ -32,13 +32,13 @@ pipeline {
remote.password = "${password}"
}
sshCommand remote: remote, command: 'pwd=$(pwd) echo "ssh连接成功当前工作目录$(pwd)"'
sshCommand remote: remote, command: 'echo "删除superManagement..."'
sshRemove remote: remote, path: '/home/app/apps/1panel/apps/openresty/openresty/www/sites/policeSecurityServer/index/superManagement'
sshCommand remote: remote, command: 'echo "删除superManagement成功!"'
sshCommand remote: remote, command: 'echo "删除super_management..."'
sshRemove remote: remote, path: '/home/app/apps/1panel/apps/openresty/openresty/www/sites/policeSecurityServer/index/super_management'
sshCommand remote: remote, command: 'echo "删除super_management成功!"'
sshCommand remote: remote, command: 'echo "将构建的superManagement发送到服务器..."'
sshPut remote: remote, from: '/var/jenkins_home/workspace/警保联动-超级后台/superManagement/superManagement', into: '/home/app/apps/1panel/apps/openresty/openresty/www/sites/policeSecurityServer/index'
sshCommand remote: remote, command: 'echo "superManagement发送成功!"'
sshCommand remote: remote, command: 'echo "将构建的super_management发送到服务器..."'
sshPut remote: remote, from: '/var/jenkins_home/workspace/警保联动-超级后台/superManagement/super_management', into: '/home/app/apps/1panel/apps/openresty/openresty/www/sites/policeSecurityServer/index'
sshCommand remote: remote, command: 'echo "super_management发送成功!"'
}
}
}

View File

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<script type="module" src="/src/assets/iconfont/iconfont.js"></script>
<title>超级后台</title>
<title>保安管理</title>
</head>
<body>
<div id="app"></div>

View File

@ -1,6 +1,6 @@
{
"name": "supermanagement",
"appName": "超级后台",
"name": "super_management",
"appName": "保安管理",
"private": true,
"version": "1.0.0",
"type": "module",
@ -14,6 +14,7 @@
"@vueuse/core": "^11.0.3",
"ant-design-vue": "^4.2.3",
"axios": "^1.7.5",
"js-md5": "^0.8.3",
"jsencrypt": "^3.3.2",
"lodash-es": "^4.17.21",
"pinia": "^2.2.2",

View File

@ -19,7 +19,6 @@ const props = withDefaults(defineProps<MapContainerProps>(), {
viewMode: "3D",
//
zoom: 11,
mapStyle: 'amap://styles/darkblue'
}
}
})

View File

@ -7,10 +7,10 @@
collapsible
>
<div v-if="!collapsed" class="title flex-center">
<div>超级后台</div>
<div>{{ appInfo.appName }}</div>
</div>
<div v-else class="logo flex-center">
<img src="@/assets/vue.svg" title="超级后台" alt="xx">
<img src="@/assets/vue.svg" :title="appInfo.appName" alt="xx">
</div>
<SystemMenus/>
</a-layout-sider>
@ -40,6 +40,8 @@ import {ref} from "vue";
import LayoutHeader from "@/components/layout/header/LayoutHeader.vue";
import SystemMenus from "@/components/layout/SystemMenus.vue";
const appInfo = __APP_INFO
const collapsed = ref<boolean>(false);
const keepAliveNames = ref<string[]>([])

View File

@ -52,13 +52,6 @@ export const SYSTEM_MENUS: SystemMenu[] = [
component: () => import('@/views/unitManage/securityUnit/index.vue')
}
]
}, {
title: '数据总览',
name: 'dataOverview',
path: '/dataOverview',
type: 'menu',
isFull: true,
component: () => import('@/views/data/dataOverview.vue')
}, {
title: '开放平台',
name: 'openPlatform',

View File

@ -1,4 +1,9 @@
declare const __APP_ENV: ImportMetaEnv;
declare const __APP_INFO: {
moduleName: string,
appName: string,
version: string
}
/**
*

View File

@ -10,7 +10,7 @@ import {ROUTER_WHITE_LIST} from "@/config";
* createWebHashHistory: 路径带# URL SEO
*/
const router = createRouter({
history: createWebHistory(__APP_ENV.VITE_APP_MODULE_NAME),
history: createWebHistory(__APP_INFO.moduleName),
routes: [...staticRouter],
strict: false,
scrollBehavior: () => ({left: 0, top: 0}),

View File

@ -11,7 +11,7 @@
</template>
<script setup lang="ts">
import {EnterprisesUnit} from "@/types/views/data/dataOverview.ts";
import {EnterprisesUnit} from "@/types/views/index.ts";
defineProps<EnterprisesUnit>()

View File

@ -1,112 +0,0 @@
<template>
<div class="container">
<div class="header">
<h1>警保联动数据总览</h1>
</div>
<MapContainer class="map-container" ref="mapRef" :init-callback="initMap">
<div class="left-panel panel">
<h2>左侧统计面板</h2>
<!-- 添加你的统计数据 -->
</div>
<div class="right-panel panel">
<h2>右侧统计面板</h2>
<!-- 添加你的统计数据 -->
</div>
</MapContainer>
</div>
</template>
<script setup lang="ts">
import MapContainer from "@/components/aMap/MapContainer.vue";
import {createApp, h, ref} from "vue";
import {ComponentExposed} from "vue-component-type-helpers";
import api from "@/axios";
import EnterprisesUnitInfoWindowContent from "@/views/data/components/EnterprisesUnitInfoWindowContent.vue";
import {EnterprisesUnit} from "@/types/views/data/dataOverview.ts";
const mapRef = ref<ComponentExposed<typeof MapContainer>>(null)
let infoWindow: AMap.InfoWindow = null;
const initMap = async () => {
const {data} = await api.get<EnterprisesUnit[]>('/enterprisesUnit/mapPoint')
mapRef.value?.mapInstance?.clearMap()
const markers = data.map(item => {
if (!item.point) {
return null;
}
const marker = new AMap.Marker({
icon: new AMap.Icon({
image: __APP_ENV.VITE_APP_MINIO_URL + item.type.extData.icon,
size: new AMap.Size(66, 66),
imageSize: new AMap.Size(66, 66),
}),
position: item.point,
offset: new AMap.Pixel(-33, -66)
})
marker.on('click', () => {
if (!infoWindow) {
infoWindow = new AMap.InfoWindow({
isCustom: true,
offset: new AMap.Pixel(66, -66),
closeWhenClickMap: true
});
}
const element = document.createElement('div')
//h vue
const _InfoWindow = h(EnterprisesUnitInfoWindowContent, item)
const app = createApp(_InfoWindow)
app.mount(element)
infoWindow.setContent(element)
infoWindow.open(mapRef.value?.mapInstance, item.point)
})
return marker
}).filter(Boolean)
mapRef.value.mapInstance.add(markers)
mapRef.value.mapInstance.setFitView()
}
</script>
<style scoped lang="scss">
.container {
position: relative;
height: 100vh;
}
.header {
background-color: rgba(255, 255, 255, 0.7);
color: #333;
text-align: center;
padding: 20px;
position: absolute;
top: 20px;
left: 0;
right: 0;
z-index: 10;
}
.map-container {
height: 100%;
position: relative;
}
.panel {
background-color: rgba(255, 255, 255, 0.7);
padding: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
position: absolute;
top: 100px; /* 调整顶部距离以避免与标题重叠 */
z-index: 10;
}
.left-panel {
width: 15%;
left: 10px;
}
.right-panel {
width: 15%;
right: 10px;
}
</style>

View File

@ -1,13 +1,112 @@
<template>
<div>
index页面
<div class="container">
<div class="header">
<h1>警保联动数据总览</h1>
</div>
<MapContainer class="map-container" ref="mapRef" :init-callback="initMap">
<div class="left-panel panel">
<h2>左侧统计面板</h2>
<!-- 添加你的统计数据 -->
</div>
<div class="right-panel panel">
<h2>右侧统计面板</h2>
<!-- 添加你的统计数据 -->
</div>
</MapContainer>
</div>
</template>
<script setup lang="ts">
import MapContainer from "@/components/aMap/MapContainer.vue";
import {createApp, h, ref} from "vue";
import {ComponentExposed} from "vue-component-type-helpers";
import api from "@/axios";
import EnterprisesUnitInfoWindowContent from "@/views/components/EnterprisesUnitInfoWindowContent.vue";
import {EnterprisesUnit} from "@/types/views/index.ts";
const mapRef = ref<ComponentExposed<typeof MapContainer>>(null)
let infoWindow: AMap.InfoWindow = null;
const initMap = async () => {
const {data} = await api.get<EnterprisesUnit[]>('/super/index/mapPoint')
mapRef.value?.mapInstance?.clearMap()
const markers = data.map(item => {
if (!item.point) {
return null;
}
const marker = new AMap.Marker({
icon: new AMap.Icon({
image: __APP_ENV.VITE_APP_MINIO_URL + item.type.extData.icon,
size: new AMap.Size(66, 66),
imageSize: new AMap.Size(66, 66),
}),
position: item.point,
offset: new AMap.Pixel(-33, -66)
})
marker.on('click', () => {
if (!infoWindow) {
infoWindow = new AMap.InfoWindow({
isCustom: true,
offset: new AMap.Pixel(66, -66),
closeWhenClickMap: true
});
}
const element = document.createElement('div')
//h vue
const _InfoWindow = h(EnterprisesUnitInfoWindowContent, item)
const app = createApp(_InfoWindow)
app.mount(element)
infoWindow.setContent(element)
infoWindow.open(mapRef.value?.mapInstance, item.point)
})
return marker
}).filter(Boolean)
mapRef.value.mapInstance.add(markers)
mapRef.value.mapInstance.setFitView()
}
</script>
<style scoped lang="scss">
.container {
position: relative;
height: 100%;
}
.header {
background-color: rgba(255, 255, 255, 0.7);
color: #333;
text-align: center;
padding: 20px;
position: absolute;
top: 20px;
left: 0;
right: 0;
z-index: 10;
}
.map-container {
height: 100%;
position: relative;
}
.panel {
background-color: rgba(255, 255, 255, 0.7);
padding: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
position: absolute;
top: 100px; /* 调整顶部距离以避免与标题重叠 */
z-index: 10;
}
.left-panel {
width: 15%;
left: 10px;
}
.right-panel {
width: 15%;
right: 10px;
}
</style>

View File

@ -29,8 +29,8 @@ import TableProMax from "@/components/table/TableProMax.vue";
import {TableProMaxProps} from "@/types/components/table";
import {AccessKeyRes, GeneratedAccessKeyParams} from "@/types/views/openPlatform/openPlatform.ts";
import {ComponentExposed} from "vue-component-type-helpers";
import {md5} from "js-md5";
import axios from "axios";
import {useUserStore} from "@/stores/modules/userStore.ts";
type TableProps = TableProMaxProps<AccessKeyRes>
@ -123,29 +123,64 @@ const saveOrUpdateAccessKey = (params: GeneratedAccessKeyParams) => {
}
})
}
const userStore = useUserStore()
const a = () => {
axios.get('http://127.0.0.1:8765/open/dataView', {
headers: {
'X-API-KEY': '123',
'access-key': '123',
'time-stamp': '123',
'sign': '123',
'nonce': '123'
const accessKey = "w2wzi0wefmmo6s735z2el8tfzitya5gj"
const secretKey = "db1b5214-02ee-497f-957c-88323b4351bf"
const now = Date.now()
const params = {
id: "1855852231820054528",
type: 'ENTERPRISES_UNIT'
}
const sign = generatedSign(params, now, accessKey, secretKey)
const headers2 = {
'Access-Key': accessKey,
'Time-Stamp': now,
'Sign': sign
}
console.log(headers2);
axios.get('http://127.0.0.1:8765/open/dataView', {
params: params,
headers: {
...headers2
}
}).then(resp => {
console.log(resp);
})
// api.get('/open/dataView', null, {
// headers: {
// 'X-API-KEY': '123',
// 'access-key': '123',
// 'time-stamp': '123',
// 'sign': '123',
// 'nonce': '123'
// }
// api.get('/open/dataView', paramsMap, {
// headers
// })
}
/**
* 生成签名
* @param params 参数
* @param timeStamp 时间戳
* @param accessKey
* @param secretKey
*/
const generatedSign = (params: Record<string, any>,
timeStamp: number,
accessKey: string,
secretKey: string): string => {
const paramsMap = new Map<string, any>();
if (params) {
for (let paramsKey in params) {
paramsMap.set(paramsKey, params[paramsKey])
}
}
paramsMap.set('Access-Key', accessKey)
paramsMap.set('Time-Stamp', timeStamp)
// Map
const entries = Array.from(paramsMap.entries());
// URL
const encodedParams = entries
.filter(([_, value]) => value !== null && value !== undefined && value !== '')
.sort((a, b) => a[0].localeCompare(b[0], undefined, {sensitivity: 'base', caseFirst: 'upper'}))
.map(([key, value]) => `${key}=${encodeURIComponent(value).replace(/%([0-9A-Fa-f]{2})/g, (_, p1) => '%' + p1.toUpperCase())}`)
.join('&') + "&Secret-Key=" + secretKey;
return md5(encodedParams).toUpperCase();
}
const allowedResources = ref<SelectNodeVo<string>[]>([])

View File

@ -6,8 +6,6 @@ interface ImportMetaEnv {
readonly VITE_APP_ENV: 'development' | 'production';
// 启动端口
readonly VITE_APP_PORT: number;
// 模块名称
readonly VITE_APP_MODULE_NAME: string;
// axios
readonly VITE_APP_BASE_API: string;

View File

@ -4,6 +4,7 @@ import Components from 'unplugin-vue-components/vite';
import {AntDesignVueResolver} from 'unplugin-vue-components/resolvers';
import * as path from "node:path";
import vueJsx from '@vitejs/plugin-vue-jsx'
import {appName, version, name as moduleName} from './package.json'
const pathSrc = path.resolve(__dirname, 'src');
@ -12,9 +13,14 @@ export default defineConfig(({mode}) => {
const env: Record<string, string> = loadEnv(mode, process.cwd(), '')
return {
define: {
__APP_ENV: JSON.stringify(env)
__APP_ENV: JSON.stringify(env),
__APP_INFO: JSON.stringify({
moduleName,
appName,
version
})
},
base: `/${env['VITE_APP_MODULE_NAME']}/`,
base: `/${moduleName}/`,
plugins: [
vue(),
vueJsx(),
@ -43,7 +49,7 @@ export default defineConfig(({mode}) => {
}
},
build: {
outDir: env['VITE_APP_MODULE_NAME'],
outDir: moduleName,
target: 'modules',
chunkSizeWarningLimit: 1500,
minify: 'terser',