lin
2024-03-12 0b6c174db343bb3b76c5deec566749f721997a74
从业人员信息录入
15 files modified
9 files added
1761 ■■■■ changed files
pom.xml 19 ●●●● patch | view | raw | blame | history
src/main/java/org/springblade/common/config/WxPayConfig.java 163 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/common/enums/OrderStatus.java 49 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/common/enums/PayType.java 24 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/common/enums/wxpay/WxApiType.java 65 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/common/enums/wxpay/WxNotifyType.java 30 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/common/enums/wxpay/WxRefundStatus.java 34 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/common/enums/wxpay/WxTradeState.java 34 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/common/utils/HttpUtils.java 39 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/common/utils/WechatPay2ValidatorForRequest.java 114 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/pay/controller/WxPayController.java 105 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/pay/service/IWxPayService.java 8 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/pay/service/impl/WxPayServiceImpl.java 248 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/place/controller/PlacePractitionerController.java 39 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/place/entity/PlaceEntity.java 230 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/place/entity/PlaceExtEntity.java 208 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/place/entity/PlacePractitionerEntity.java 104 ●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/place/mapper/PlaceExtMapper.xml 93 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/place/mapper/PlaceMapper.xml 5 ●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/place/mapper/PlacePractitionerMapper.java 8 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/place/mapper/PlacePractitionerMapper.xml 118 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/place/service/IPlacePractitionerService.java 1 ●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/place/service/impl/PlacePractitionerServiceImpl.java 19 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/place/vo/PlacePractitionerVO.java 4 ●●●● patch | view | raw | blame | history
pom.xml
@@ -195,11 +195,24 @@
            <version>1.13</version>
        </dependency>
        <!-- 微信支付 SDK-->
<!--        <dependency>-->
<!--            <groupId>com.github.binarywang</groupId>-->
<!--            <artifactId>weixin-java-pay</artifactId>-->
<!--            <version>4.4.9.B</version>-->
<!--        </dependency>-->
        <!-- 微信支付依赖 -->
        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-pay</artifactId>
            <version>4.4.9.B</version>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.3.0</version>
        </dependency>
        <!--微信支付 APIv2 SDK-->
        <dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>0.0.3</version>
        </dependency>
        <!--xxl-job-core 相关依赖-->
        <dependency>
            <groupId>io.netty</groupId>
src/main/java/org/springblade/common/config/WxPayConfig.java
New file
@@ -0,0 +1,163 @@
package org.springblade.common.config;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.ScheduledUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
//@Configuration
// @PropertySource("classpath:wxpay.properties") //读取配置文件
//@ConfigurationProperties(prefix = "wxpay") //读取wxpay节点
@Data //使用set方法将wxpay节点中的值填充到当前类的属性中
@Slf4j
public class WxPayConfig {
    // 商户号
    private String mchId;
    // 商户API证书序列号
    private String mchSerialNo;
    // 商户私钥文件
    private String privateKeyPath;
    private String privateCertPath;
    // APIv3密钥
    private String apiV3Key;
    // APPID
    private String appid;
    // 微信服务器地址
    private String domain;
    // 接收结果通知地址
    private String notifyDomain;
    // APIv2密钥
    private String partnerKey;
    /**
     * 获取商户的私钥文件
     *
     * @param filename
     * @return
     */
    private PrivateKey getPrivateKey(String filename) {
        try {
            // FileInputStream fileInputStream = new FileInputStream(filename);
            // log.info("文件内容:" + fileInputStream);
            return PemUtil.loadPrivateKey(getFileInputStream(filename));
        } catch (Exception e) {
            log.info("私钥文件不存在", e);
            throw new RuntimeException("私钥文件不存在", e);
        }
    }
    /**
     * 获取商户的文件
     *
     * @param filename
     * @return
     */
    public static FileInputStream getFileInputStream(String filename) {
        try {
            FileInputStream fileInputStream = new FileInputStream(filename);
            return fileInputStream;
        } catch (Exception e) {
            log.info("读取文件不存在", e);
            throw new RuntimeException("读取文件不存在", e);
        }
    }
    /**
     * 获取商户的文件
     *
     * @return
     */
    // @Bean
    // public KeyStore keyStore() {
    //     try {
    //         InputStream certStream = WxPayConfig.class.getClassLoader().getResourceAsStream(privateCertPath);
    //         KeyStore ks = KeyStore.getInstance("PKCS12");
    //         ks.load(certStream, mchId.toCharArray());
    //         return ks;
    //     } catch (Exception e) {
    //         log.info("读取文件不存在", e);
    //         throw new RuntimeException("读取文件不存在", e);
    //     }
    // }
    /**
     * 获取签名验证器
     *
     * @return
     */
//    @Bean
    public ScheduledUpdateCertificatesVerifier getVerifier(String privateKeyPath, String mchSerialNo, String mchId, String apiV3Key) {
        log.info("获取签名验证器");
        //获取商户私钥
        PrivateKey privateKey = getPrivateKey(privateKeyPath);
        //私钥签名对象
        PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, privateKey);
        //身份认证对象
        WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);
        // 使用定时更新的签名验证器,不需要传入证书
        ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(
            wechatPay2Credentials,
            apiV3Key.getBytes(StandardCharsets.UTF_8));
        return verifier;
    }
    /**
     * 获取http请求对象
     *
     * @param verifier
     * @return
     */
//    @Bean(name = "wxPayClient")
    public CloseableHttpClient getWxPayClient(String privateKeyPath, String mchSerialNo, String mchId, String apiV3Key) {
        log.info("获取httpClient");
        //获取商户私钥
        PrivateKey privateKey = getPrivateKey(privateKeyPath);
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
            .withMerchant(mchId, mchSerialNo, privateKey)
            .withValidator(new WechatPay2Validator(getVerifier(privateKeyPath, mchSerialNo, mchId, apiV3Key)));
        // ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient
        // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();
        return httpClient;
    }
}
src/main/java/org/springblade/common/enums/OrderStatus.java
New file
@@ -0,0 +1,49 @@
package org.springblade.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum OrderStatus {
    /**
     * 未支付
     */
    NOTPAY("未支付"),
    /**
     * 支付成功
     */
    SUCCESS("支付成功"),
    /**
     * 已关闭
     */
    CLOSED("超时已关闭"),
    /**
     * 已取消
     */
    CANCEL("用户已取消"),
    /**
     * 退款中
     */
    REFUND_PROCESSING("退款中"),
    /**
     * 已退款
     */
    REFUND_SUCCESS("已退款"),
    /**
     * 退款异常
     */
    REFUND_ABNORMAL("退款异常");
    /**
     * 类型
     */
    private final String type;
}
src/main/java/org/springblade/common/enums/PayType.java
New file
@@ -0,0 +1,24 @@
package org.springblade.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum PayType {
    /**
     * 微信
     */
    WXPAY("微信"),
    /**
     * 支付宝
     */
    ALIPAY("支付宝");
    /**
     * 类型
     */
    private final String type;
}
src/main/java/org/springblade/common/enums/wxpay/WxApiType.java
New file
@@ -0,0 +1,65 @@
package org.springblade.common.enums.wxpay;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum WxApiType {
    /**
     * Native下单
     */
    NATIVE_PAY("/v3/pay/transactions/native"),
    /**
     * Native下单
     */
    NATIVE_PAY_V2("/pay/unifiedorder"),
    /**
     * JSAPI下单
     */
    JSAPI_PAY("/v3/pay/transactions/jsapi"),
    /**
     * sendredpack 发送红包
     */
    SENDREDPACK_PAY("/mmpaymkttransfers/sendredpack"),
    /**
     * 查询订单
     */
    ORDER_QUERY_BY_NO("/v3/pay/transactions/out-trade-no/%s"),
    /**
     * 关闭订单
     */
    CLOSE_ORDER_BY_NO("/v3/pay/transactions/out-trade-no/%s/close"),
    /**
     * 申请退款
     */
    DOMESTIC_REFUNDS("/v3/refund/domestic/refunds"),
    /**
     * 查询单笔退款
     */
    DOMESTIC_REFUNDS_QUERY("/v3/refund/domestic/refunds/%s"),
    /**
     * 申请交易账单
     */
    TRADE_BILLS("/v3/bill/tradebill"),
    /**
     * 申请资金账单
     */
    FUND_FLOW_BILLS("/v3/bill/fundflowbill");
    /**
     * 类型
     */
    private final String type;
}
src/main/java/org/springblade/common/enums/wxpay/WxNotifyType.java
New file
@@ -0,0 +1,30 @@
package org.springblade.common.enums.wxpay;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum WxNotifyType {
    /**
     * 支付通知
     */
    NATIVE_NOTIFY("/wxPay/native/notify"),
    /**
     * 支付通知
     */
    NATIVE_NOTIFY_V2("/wxPayV2/native/notify"),
    /**
     * 退款结果通知
     */
    REFUND_NOTIFY("/wxPay/refunds/notify");
    /**
     * 类型
     */
    private final String type;
}
src/main/java/org/springblade/common/enums/wxpay/WxRefundStatus.java
New file
@@ -0,0 +1,34 @@
package org.springblade.common.enums.wxpay;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum WxRefundStatus {
    /**
     * 退款成功
     */
    SUCCESS("SUCCESS"),
    /**
     * 退款关闭
     */
    CLOSED("CLOSED"),
    /**
     * 退款处理中
     */
    PROCESSING("PROCESSING"),
    /**
     * 退款异常
     */
    ABNORMAL("ABNORMAL");
    /**
     * 类型
     */
    private final String type;
}
src/main/java/org/springblade/common/enums/wxpay/WxTradeState.java
New file
@@ -0,0 +1,34 @@
package org.springblade.common.enums.wxpay;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum WxTradeState {
    /**
     * 支付成功
     */
    SUCCESS("SUCCESS"),
    /**
     * 未支付
     */
    NOTPAY("NOTPAY"),
    /**
     * 已关闭
     */
    CLOSED("CLOSED"),
    /**
     * 转入退款
     */
    REFUND("REFUND");
    /**
     * 类型
     */
    private final String type;
}
src/main/java/org/springblade/common/utils/HttpUtils.java
New file
@@ -0,0 +1,39 @@
package org.springblade.common.utils;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
public class HttpUtils {
    /**
     * 将通知参数转化为字符串
     * @param request
     * @return
     */
    public static String readData(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            StringBuilder result = new StringBuilder();
            br = request.getReader();
            for (String line; (line = br.readLine()) != null; ) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append(line);
            }
            return result.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
src/main/java/org/springblade/common/utils/WechatPay2ValidatorForRequest.java
New file
@@ -0,0 +1,114 @@
package org.springblade.common.utils;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.*;
/**
 * @author xy-peng
 */
public class WechatPay2ValidatorForRequest {
    protected static final Logger log = LoggerFactory.getLogger(WechatPay2ValidatorForRequest.class);
    /**
     * 应答超时时间,单位为分钟
     */
    protected static final long RESPONSE_EXPIRED_MINUTES = 5;
    protected final Verifier verifier;
    protected final String requestId;
    protected final String body;
    public WechatPay2ValidatorForRequest(Verifier verifier, String requestId, String body) {
        this.verifier = verifier;
        this.requestId = requestId;
        this.body = body;
    }
    protected static IllegalArgumentException parameterError(String message, Object... args) {
        message = String.format(message, args);
        return new IllegalArgumentException("parameter error: " + message);
    }
    protected static IllegalArgumentException verifyFail(String message, Object... args) {
        message = String.format(message, args);
        return new IllegalArgumentException("signature verify fail: " + message);
    }
    public final boolean validate(HttpServletRequest request) throws IOException {
        try {
            //处理请求参数
            validateParameters(request);
            //构造验签名串
            String message = buildMessage(request);
            String serial = request.getHeader(WECHAT_PAY_SERIAL);
            String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
            //验签
            if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {
                throw verifyFail("serial=[%s] message=[%s] sign=[%s], request-id=[%s]",
                        serial, message, signature, requestId);
            }
        } catch (IllegalArgumentException e) {
            log.warn(e.getMessage());
            return false;
        }
        return true;
    }
    protected final void validateParameters(HttpServletRequest request) {
        // NOTE: ensure HEADER_WECHAT_PAY_TIMESTAMP at last
        String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};
        String header = null;
        for (String headerName : headers) {
            header = request.getHeader(headerName);
            if (header == null) {
                throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);
            }
        }
        //判断请求是否过期
        String timestampStr = header;
        try {
            Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));
            // 拒绝过期请求
            if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {
                throw parameterError("timestamp=[%s] expires, request-id=[%s]", timestampStr, requestId);
            }
        } catch (DateTimeException | NumberFormatException e) {
            throw parameterError("invalid timestamp=[%s], request-id=[%s]", timestampStr, requestId);
        }
    }
    protected final String buildMessage(HttpServletRequest request) throws IOException {
        String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
        String nonce = request.getHeader(WECHAT_PAY_NONCE);
        return timestamp + "\n"
                + nonce + "\n"
                + body + "\n";
    }
    protected final String getResponseBody(CloseableHttpResponse response) throws IOException {
        HttpEntity entity = response.getEntity();
        return (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : "";
    }
}
src/main/java/org/springblade/modules/pay/controller/WxPayController.java
@@ -1,23 +1,33 @@
package org.springblade.modules.pay.controller;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.google.gson.Gson;
import com.wechat.pay.contrib.apache.httpclient.auth.ScheduledUpdateCertificatesVerifier;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.common.config.WxPayConfig;
import org.springblade.common.utils.HttpUtils;
import org.springblade.common.utils.WechatPay2ValidatorForRequest;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.tool.api.R;
import org.springblade.modules.pay.entity.WxPayInfo;
import org.springblade.modules.pay.service.IWxPayService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@RestController
@AllArgsConstructor
@RequestMapping("/wxpay")
@Api(value = "微信支付接口", tags = "微信支付接口")
public class WxPayController extends BladeController {
    private static final Logger logger = LoggerFactory.getLogger(WxPayController.class);
    private IWxPayService wxPayService;
@@ -38,12 +48,91 @@
    /**
     * 获取openId
     *
     * @param code
     * @return
     */
    @PostMapping("getOpenId")
    public R getOpenId(String code){
         return R.data(wxPayService.getOpenId(code));
    public R getOpenId(String code) {
        return R.data(wxPayService.getOpenId(code));
    }
    /**
     * jsapi下单
     *
     * @param productId
     * @return
     * @throws Exception
     */
    @ApiOperation("调用jsapi统一下单API")
    @GetMapping("/jsapiPay")
    public R jsapiPay(@RequestParam("productId") Long productId) throws Exception {
        logger.info("发起支付请求 v3");
        //返回支付二维码连接和订单号
        Map<String, String> map = wxPayService.jsapiPay(productId);
        return R.data(map);
    }
    /**
     * 支付通知
     * 微信支付通过支付通知接口将用户支付成功消息通知给商户
     */
    @ApiOperation("支付通知")
    @PostMapping("/native/notify")
    public String nativeNotify(HttpServletRequest request, HttpServletResponse response) {
        Gson gson = new Gson();
        Map<String, String> map = new HashMap<>();//应答对象
        try {
            //处理通知参数
            String body = HttpUtils.readData(request);
            Map<String, Object> bodyMap = gson.fromJson(body, HashMap.class);
            String requestId = (String) bodyMap.get("id");
            logger.info("支付通知的id ===> {}", requestId);
            logger.info("支付通知的完整数据 ===> {}", body);
            //int a = 9 / 0;
            ScheduledUpdateCertificatesVerifier verifier = new WxPayConfig().getVerifier("111", "111", "111", "111");
            //签名的验证
            WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest
                = new WechatPay2ValidatorForRequest(verifier, requestId, body);
            if (!wechatPay2ValidatorForRequest.validate(request)) {
                logger.error("通知验签失败");
                //失败应答
                response.setStatus(500);
                map.put("code", "ERROR");
                map.put("message", "通知验签失败");
                return gson.toJson(map);
            }
            logger.info("通知验签成功");
            //处理订单
            wxPayService.processOrder(bodyMap);
            //应答超时
            //模拟接收微信端的重复通知
            // TimeUnit.SECONDS.sleep(5);
            //成功应答
            response.setStatus(200);
            map.put("code", "SUCCESS");
            map.put("message", "成功");
            return gson.toJson(map);
        } catch (Exception e) {
            e.printStackTrace();
            //失败应答
            response.setStatus(500);
            map.put("code", "ERROR");
            map.put("message", "失败");
            return gson.toJson(map);
        }
    }
}
src/main/java/org/springblade/modules/pay/service/IWxPayService.java
@@ -3,6 +3,10 @@
import com.baomidou.mybatisplus.extension.service.IService;
import org.springblade.modules.pay.entity.WxPayInfo;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Map;
public interface IWxPayService extends IService<WxPayInfo> {
    /**
@@ -11,4 +15,8 @@
     * @return
     */
    Object getOpenId(String code);
    Map<String, String> jsapiPay(Long productId) throws IOException;
    void processOrder(Map<String, Object> bodyMap) throws GeneralSecurityException;
}
src/main/java/org/springblade/modules/pay/service/impl/WxPayServiceImpl.java
@@ -1,25 +1,261 @@
package org.springblade.modules.pay.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.wxpay.sdk.WXPayUtil;
import com.google.gson.Gson;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.common.enums.OrderStatus;
import org.springblade.common.enums.wxpay.WxApiType;
import org.springblade.common.enums.wxpay.WxNotifyType;
import org.springblade.modules.pay.controller.WxPayController;
import org.springblade.modules.pay.entity.WxPayInfo;
import org.springblade.modules.pay.mapper.WxPayMapper;
import org.springblade.modules.pay.service.IWxPayService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
@Service
public class WxPayServiceImpl extends ServiceImpl<WxPayMapper,WxPayInfo> implements IWxPayService {
    private final String  MINI_PROGRAM_APP_ID = "wx41aa8a5d2e565a05";
    private static final Logger logger = LoggerFactory.getLogger(WxPayServiceImpl.class);
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public Object getOpenId(String code) {
        return null;
    }
    @Override
    public Map<String, String> jsapiPay(Long productId) throws IOException {
        String prepayId = "";
        //生成订单
//        OrderInfo orderInfo = orderInfoService.createOrderByProductId(productId);
        logger.info("调用统一下单API");
//        //调用统一下单API
//        HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(WxApiType.JSAPI_PAY.getType()));
//
//        // 请求body参数
//
//        Gson gson = new Gson();
//        Map paramsMap = new HashMap();
//        paramsMap.put("appid", wxPayConfig.getAppid());
//        paramsMap.put("mchid", wxPayConfig.getMchId());
//        paramsMap.put("description", orderInfo.getTitle());
//        paramsMap.put("out_trade_no", orderInfo.getOrderNo());
//        paramsMap.put("notify_url", wxPayConfig.getNotifyDomain().concat(WxNotifyType.NATIVE_NOTIFY.getType()));
//
//        Map amountMap = new HashMap();
//        amountMap.put("total", orderInfo.getTotalFee());
//        amountMap.put("currency", "CNY");
//
//        paramsMap.put("amount", amountMap);
//
//        Map payerMap = new HashMap();
//        payerMap.put("openid", "oSX3G6OUuGaKJbWzSHUpPbzgtDXo");
//
//        paramsMap.put("payer", payerMap);
//
//        //将参数转换成json字符串
//        String jsonParams = gson.toJson(paramsMap);
//        logger.info("请求参数 ===> {}" + jsonParams);
//
//        StringEntity entity = new StringEntity(jsonParams, "utf-8");
//        entity.setContentType("application/json");
//        httpPost.setEntity(entity);
//        httpPost.setHeader("Accept", "application/json");
//
//        //完成签名并执行请求
//        CloseableHttpResponse response = wxPayClient.execute(httpPost);
//
//        try {
//            String bodyAsString = EntityUtils.toString(response.getEntity());//响应体
//            int statusCode = response.getStatusLine().getStatusCode();//响应状态码
//            if (statusCode == 200) { //处理成功
//                logger.info("成功, 返回结果 = " + bodyAsString);
//            } else if (statusCode == 204) { //处理成功,无返回Body
//                logger.info("成功");
//            } else {
//                logger.info("jszpi下单失败,响应码 = " + statusCode + ",返回结果 = " + bodyAsString);
//                throw new IOException("request failed");
//            }
//            //响应结果
//            Map<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
//            //预支付交易会话标识
//            prepayId = resultMap.get("prepay_id");
//
//            //返回预支付交易会话标识
//            // 组装前端预下单参数
//            /** 返回给前端所需要的数据 */
//            SortedMap<String, String> payMap = new TreeMap<>();
//            String nonceStr = WXPayUtil.generateNonceStr();
//            String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
//            payMap.put("appId", wxPayConfig.getAppid());
//            payMap.put("timeStamp", timestamp);
//            payMap.put("nonceStr", nonceStr);
//            payMap.put("signType", "RSA");
//            payMap.put("package", "prepay_id=" + prepayId);
//            String paySign = getSign(wxPayConfig.getAppid(), prepayId, timestamp, nonceStr);
//            payMap.put("paySign", paySign);
//            logger.info("返回参数 ===> {}" + gson.toJson(payMap));
//            return payMap;
//        } finally {
//            response.close();
//        }
        return null;
    }
    @Override
    public void processOrder(Map<String, Object> bodyMap) throws GeneralSecurityException {
        logger.info("处理订单");
        //解密报文
        String plainText = decryptFromResource(bodyMap);
        //将明文转换成map
        Gson gson = new Gson();
        HashMap plainTextMap = gson.fromJson(plainText, HashMap.class);
        String orderNo = (String) plainTextMap.get("out_trade_no");
        /*在对业务数据进行状态检查和处理之前,
        要采用数据锁进行并发控制,
        以避免函数重入造成的数据混乱*/
        //尝试获取锁:
        // 成功获取则立即返回true,获取失败则立即返回false。不必一直等待锁的释放
//        if (lock.tryLock()) {
//            try {
//                //处理重复的通知
//                //接口调用的幂等性:无论接口被调用多少次,产生的结果是一致的。
//                String orderStatus = orderInfoService.getOrderStatus(orderNo);
//                if (!OrderStatus.NOTPAY.getType().equals(orderStatus)) {
//                    return;
//                }
//
//                //模拟通知并发
//                try {
//                    TimeUnit.SECONDS.sleep(5);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//
//                //更新订单状态
//                orderInfoService.updateStatusByOrderNo(orderNo, OrderStatus.SUCCESS);
//
//                //记录支付日志
//                paymentInfoService.createPaymentInfo(plainText);
//            } finally {
//                //要主动释放锁
//                lock.unlock();
//            }
//        }
    }
    /**
     * 通过prepay_id获取签名
     *
     * @param appid
     * @param prepay_id
     * @param timestamp
     * @param nonceStr
     * @return
     * @throws IOException
     * @throws SignatureException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     */
    String getSign(String appid, String prepay_id, String timestamp, String nonceStr) throws IOException, SignatureException, NoSuchAlgorithmException, InvalidKeyException {
        //从下往上依次生成
        String message = getSignStr(appid, timestamp, nonceStr, "prepay_id=" + prepay_id);
        //签名
        String signature = sign(message.getBytes("utf-8"));
        return signature;
    }
    String sign(byte[] message) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {
        //签名方式
        Signature sign = Signature.getInstance("SHA256withRSA");
        //私钥,通过MyPrivateKey来获取,这是个静态类可以接调用方法 ,需要的是_key.pem文件的绝对路径配上文件名
//        sign.initSign(getPrivateKey(wxPayConfig.getPrivateKeyPath()));
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }
    /**
     * 按照前端签名文档规范进行排序
     *
     * @param appId
     * @param timeStamp
     * @param nonceStr
     * @param packageValue
     * @return
     */
    private String getSignStr(String appId, String timeStamp, String nonceStr, String packageValue) {
        return String.format("%s\n%s\n%s\n%s\n", appId, timeStamp, nonceStr, packageValue);
    }
    private PrivateKey getPrivateKey(String filename) {
        try {
            FileInputStream fileInputStream = new FileInputStream(filename);
            logger.info("文件内容:" + fileInputStream);
            return PemUtil.loadPrivateKey(fileInputStream);
        } catch (Exception e) {
            logger.info("私钥文件不存在", e);
            throw new RuntimeException("私钥文件不存在", e);
        }
    }
    /**
     * 对称解密
     *
     * @param bodyMap
     * @return
     */
    private String decryptFromResource(Map<String, Object> bodyMap) throws GeneralSecurityException {
        logger.info("密文解密");
        //通知数据
        Map<String, String> resourceMap = (Map) bodyMap.get("resource");
        //数据密文
        String ciphertext = resourceMap.get("ciphertext");
        //随机串
        String nonce = resourceMap.get("nonce");
        //附加数据
        String associatedData = resourceMap.get("associated_data");
        logger.info("密文 ===> {}", ciphertext);
//        AesUtil aesUtil = new AesUtil(wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
//        String plainText = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
//            nonce.getBytes(StandardCharsets.UTF_8),
//            ciphertext);
//        logger.info("明文 ===> {}", plainText);
//        return plainText;
        return null;
    }
}
src/main/java/org/springblade/modules/place/controller/PlacePractitionerController.java
@@ -21,12 +21,15 @@
import io.swagger.annotations.ApiParam;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import lombok.AllArgsConstructor;
import javax.validation.Valid;
import org.apache.commons.lang3.StringUtils;
import org.springblade.core.secure.BladeUser;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.Func;
import org.springframework.web.bind.annotation.*;
import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -35,6 +38,8 @@
import org.springblade.modules.place.wrapper.PlacePractitionerWrapper;
import org.springblade.modules.place.service.IPlacePractitionerService;
import org.springblade.core.boot.ctrl.BladeController;
import java.util.Date;
/**
 * 场所从业人员 控制器
@@ -46,7 +51,7 @@
@AllArgsConstructor
@RequestMapping("blade-placePractitioner/placePractitioner")
@Api(value = "场所从业人员", tags = "场所从业人员接口")
public class PlacePractitionerController{
public class PlacePractitionerController {
    private final IPlacePractitionerService placePractitionerService;
@@ -60,6 +65,7 @@
        PlacePractitionerEntity detail = placePractitionerService.getOne(Condition.getQueryWrapper(placePractitioner));
        return R.data(PlacePractitionerWrapper.build().entityVO(detail));
    }
    /**
     * 场所从业人员 分页
     */
@@ -89,6 +95,13 @@
    @ApiOperationSupport(order = 4)
    @ApiOperation(value = "新增", notes = "传入placePractitioner")
    public R save(@Valid @RequestBody PlacePractitionerEntity placePractitioner) {
        if (StringUtils.isNotBlank(placePractitioner.getIdCard())) {
            // 从身份证号获取出生日期
            String birthDateStr = placePractitioner.getIdCard().substring(6, 14);
            // 解析出生日期字符串为日期对象
            Date birthDate = DateUtil.parse(birthDateStr, "yyyyMMdd");
            placePractitioner.setBirthday(birthDate);
        }
        return R.status(placePractitionerService.save(placePractitioner));
    }
@@ -99,6 +112,13 @@
    @ApiOperationSupport(order = 5)
    @ApiOperation(value = "修改", notes = "传入placePractitioner")
    public R update(@Valid @RequestBody PlacePractitionerEntity placePractitioner) {
        if (StringUtils.isNotBlank(placePractitioner.getIdCard())) {
            // 从身份证号获取出生日期
            String birthDateStr = placePractitioner.getIdCard().substring(6, 14);
            // 解析出生日期字符串为日期对象
            Date birthDate = DateUtil.parse(birthDateStr, "yyyyMMdd");
            placePractitioner.setBirthday(birthDate);
        }
        return R.status(placePractitionerService.updateById(placePractitioner));
    }
@@ -109,6 +129,13 @@
    @ApiOperationSupport(order = 6)
    @ApiOperation(value = "新增或修改", notes = "传入placePractitioner")
    public R submit(@Valid @RequestBody PlacePractitionerEntity placePractitioner) {
        if (StringUtils.isNotBlank(placePractitioner.getIdCard())) {
            // 从身份证号获取出生日期
            String birthDateStr = placePractitioner.getIdCard().substring(6, 14);
            // 解析出生日期字符串为日期对象
            Date birthDate = DateUtil.parse(birthDateStr, "yyyyMMdd");
            placePractitioner.setBirthday(birthDate);
        }
        return R.status(placePractitionerService.saveOrUpdate(placePractitioner));
    }
@@ -122,5 +149,15 @@
        return R.status(placePractitionerService.removeByIds(Func.toLongList(ids)));
    }
    /**
     * 统计少数民族和未成年数量
     */
    @PostMapping("/countByType")
    @ApiOperationSupport(order = 8)
    @ApiOperation(value = "统计少数民族和未成年数量", notes = "")
    public R countByType() {
        return R.data(placePractitionerService.countByType());
    }
}
src/main/java/org/springblade/modules/place/entity/PlaceEntity.java
@@ -43,186 +43,158 @@
public class PlaceEntity implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 主键
     */
    @JsonSerialize(using = ToStringSerializer.class)
    @ApiModelProperty("主键id")
    /** 主键id */
    @ApiModelProperty(value = "主键ID", example = "")
    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private Long id;
    /**
     * 门牌地址编码
     */
    @ApiModelProperty(value = "门牌地址编码")
    /** 门牌地址编码 */
    @ApiModelProperty(value = "门牌地址编码", example = "")
    @TableField("house_code")
    private String houseCode;
    /**
     * 门牌地址编码绑定
     */
    @ApiModelProperty(value = "门牌地址编码绑定")
    /** 门牌地址编号绑定 */
    @ApiModelProperty(value = "门牌地址编号绑定", example = "")
    @TableField("house_code_binds")
    private String houseCodeBinds;
    /**
     * 楼栋编码
     */
    @ApiModelProperty(value = "楼栋编码")
    /** 楼栋编码 */
    @ApiModelProperty(value = "楼栋编码", example = "")
    @TableField("building_code")
    private String buildingCode;
    /**
     * 场所负责人(关联用户表信息user_id)
     */
    @ApiModelProperty(value = "场所负责人(关联用户表信息user_id)")
    @JsonSerialize(using = ToStringSerializer.class)
    /** 场所负责人(关联用户表信息) */
    @ApiModelProperty(value = "场所负责人(关联用户表信息)", example = "")
    @TableField("principal_user_id")
    private Long principalUserId;
    /**
     * 场所负责人
     */
    @ApiModelProperty(value = "场所负责人")
    @JsonSerialize(using = ToStringSerializer.class)
    /** 场所负责人姓名 */
    @ApiModelProperty(value = "场所负责人姓名", example = "")
    @TableField("principal")
    private String principal;
    /** 场所负责人联系方式 */
    @ApiModelProperty(value = "场所负责人联系方式", example = "")
    @TableField("principal_phone")
    private String principalPhone;
    /** 场所负责人身份证号 */
    @ApiModelProperty(value = "场所负责人身份证号", example = "")
    @TableField("principal_id_card")
    private String principalIdCard;
    /**
     * 场所负责人联系电话
     */
    @ApiModelProperty(value = "场所负责人联系电话")
    @JsonSerialize(using = ToStringSerializer.class)
    private String principalPhone;
    /**
     * 场所名称
     */
    @ApiModelProperty(value = "场所名称")
    /** 场所名称 */
    @ApiModelProperty(value = "场所名称", example = "")
    @TableField("place_name")
    private String placeName;
    /**
     * 经度
     */
    @ApiModelProperty(value = "经度")
    /** 经度 */
    @ApiModelProperty(value = "经度", example = "")
    @TableField("lng")
    private String lng;
    /**
     * 纬度
     */
    @ApiModelProperty(value = "纬度")
    /** 纬度 */
    @ApiModelProperty(value = "纬度", example = "")
    @TableField("lat")
    private String lat;
    /**
     * 位置
     */
    @ApiModelProperty(value = "位置")
    /** 位置 */
    @ApiModelProperty(value = "位置", example = "")
    @TableField("location")
    private String location;
    /**
     * 场所照片
     */
    @ApiModelProperty(value = "场所照片")
    /** 场所照片 */
    @ApiModelProperty(value = "场所照片", example = "")
    @TableField("image_urls")
    private String imageUrls;
    /**
     * 网格编号
     */
    @ApiModelProperty(value = "网格编号")
    /** 网格id */
    @ApiModelProperty(value = "网格id", example = "")
    @TableField("grid_id")
    private Integer gridId;
    /** 网格编号 */
    @ApiModelProperty(value = "网格编号", example = "")
    @TableField("grid_code")
    private String gridCode;
    /**
     * 警务网格编号
     */
    @ApiModelProperty(value = "警务网格编号")
    /** 警务网格编号 */
    @ApiModelProperty(value = "警务网格编号", example = "")
    @TableField("jw_grid_code")
    private String jwGridCode;
    /**
     * 状态  1:待完善  2:已完善
     */
    @ApiModelProperty(value = "状态  1:待完善  2:已完善")
    private Integer status;
    /**
     * 来源 1:地址总表  2:国控采集  3:商超
     */
    @ApiModelProperty(value = "来源 1:地址总表  2:国控采集 3:商超")
    /** 来源 1:地址总表  2:国控采集 3:商超 */
    @ApiModelProperty(value = "来源 1:地址总表  2:国控采集 3:商超", example = "")
    @TableField("source")
    private Integer source;
    /**
     * 是否现场采集  1:是  2:否
     */
    @ApiModelProperty(value = "是否现场采集  1:是  2:否")
    /** 状态  1:待完善  2:已完善 */
    @ApiModelProperty(value = "状态  1:待完善  2:已完善", example = "")
    @TableField("status")
    private Integer status;
    /** 是否现场采集  1:是  2:否 */
    @ApiModelProperty(value = "是否现场采集  1:是  2:否", example = "")
    @TableField("is_scene")
    private Integer isScene;
    /**
     * 是否九小场所  1:是 2:否
     */
    @ApiModelProperty(value = "是否九小场所  1:是 2:否")
    /** 是否九小场所  1:是 2:否 */
    @ApiModelProperty(value = "是否九小场所  1:是 2:否", example = "")
    @TableField("is_nine")
    private Integer isNine;
    /**
     * 九小场所类型 字典 nineType
     */
    @ApiModelProperty(value = "九小场所类型")
    /** 九小场所类型 业务字典:nineType */
    @ApiModelProperty(value = "九小场所类型 业务字典:nineType", example = "")
    @TableField("nine_type")
    private Integer nineType;
    /**
     * 是否阵地  1:是 2:否
     */
    @ApiModelProperty(value = "是否阵地  1:是 2:否")
    /** 是否阵地  1:是 2:否 */
    @ApiModelProperty(value = "是否阵地  1:是 2:否", example = "")
    @TableField("is_front")
    private Integer isFront;
    /**
     * 阵地类型 字典 frontType
     */
    @ApiModelProperty(value = "阵地类型")
    /** 阵地类型 业务字典:frontType */
    @ApiModelProperty(value = "阵地类型 业务字典:frontType", example = "")
    @TableField("front_type")
    private Integer frontType;
    /**
     * 创建人
     */
    @JsonSerialize(using = ToStringSerializer.class)
    @ApiModelProperty("创建人")
    @TableField(fill = FieldFill.INSERT)
    /** 创建人 */
    @ApiModelProperty(value = "创建人", example = "")
    @TableField("create_user")
    private Long createUser;
    /**
     * 创建时间
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("创建时间")
    @TableField(fill = FieldFill.INSERT)
    /** 创建时间 */
    @ApiModelProperty(value = "创建时间", example = "")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField(value = "create_time",fill = FieldFill.INSERT)
    private Date createTime;
    /**
     * 更新人
     */
    @JsonSerialize(using = ToStringSerializer.class)
    @ApiModelProperty("更新人")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    /** 更新人 */
    @ApiModelProperty(value = "更新人", example = "")
    @TableField("update_user")
    private Long updateUser;
    /**
     * 更新时间
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("更新时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    /** 更新时间 */
    @ApiModelProperty(value = "更新时间", example = "")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField(value = "update_time",fill = FieldFill.UPDATE)
    private Date updateTime;
    /**
     * 备注
     */
    @ApiModelProperty(value = "备注")
    /** 备注 */
    @ApiModelProperty(value = "备注", example = "")
    @TableField("remark")
    private String remark;
    @ApiModelProperty(value = "小区编码")
    /** 是否删除 0:否  1:是 */
    @ApiModelProperty(value = "是否删除 0:否  1:是", example = "")
    @TableField("is_deleted")
    private Integer isDeleted;
    /** 小区编码 */
    @ApiModelProperty(value = "小区编码", example = "")
    @TableField("aoi_code")
    private String aoiCode;
    /**
     * 是否删除
     */
    @TableLogic
    @ApiModelProperty("是否已删除 0:否  1:是")
    private Integer isDeleted;
}
src/main/java/org/springblade/modules/place/entity/PlaceExtEntity.java
@@ -49,102 +49,156 @@
    @ApiModelProperty("主键id")
    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private Long id;
//
//    /**
//     * 任务id
//     */
//    @ApiModelProperty(value = "任务id")
//    private Long taskId;
    /**
     * 场所ID
     */
    @ApiModelProperty(value = "场所ID")
    /** 场所ID */
    @ApiModelProperty(value = "场所ID", example = "")
    @TableField("place_id")
    private Long placeId;
    /**
     * 营业执照图片URLS
     */
    @ApiModelProperty(value = "营业执照图片URLS")
    /** 营业执照图片URLS */
    @ApiModelProperty(value = "营业执照图片URLS", example = "")
    @TableField("image_urls")
    private String imageUrls;
    /**
     * 法人信息
     */
    @ApiModelProperty(value = "法人信息")
    /** 法人信息 */
    @ApiModelProperty(value = "法人信息", example = "")
    @TableField("legal_person")
    private String legalPerson;
    /**
     * 法人电话
     */
    @ApiModelProperty(value = "法人电话")
    /** 法人电话 */
    @ApiModelProperty(value = "法人电话", example = "")
    @TableField("legal_tel")
    private String legalTel;
    /**
     * 场所平面图URLS
     */
    @ApiModelProperty(value = "场所平面图URLS")
    /** 场所平面图URLS */
    @ApiModelProperty(value = "场所平面图URLS", example = "")
    @TableField("plan_image_urls")
    private String planImageUrls;
    /**
     * 确认用户ID
     */
    @ApiModelProperty(value = "确认用户ID")
    @JsonSerialize(using = ToStringSerializer.class)
    /** 审核确认人用户id */
    @ApiModelProperty(value = "审核确认人用户id", example = "")
    @TableField("confirm_user_id")
    private Long confirmUserId;
    /**
     * 确认时间
     */
    @ApiModelProperty(value = "确认时间")
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date confirmTime;
    /**
     * 确认标记 1:待审核  2:审核通过  3:审核不通过 4:待完善(地址表数据,无场所负责人)
     */
    @ApiModelProperty(value = "确认标记 1:待审核  2:审核通过  3:审核不通过 4:待完善")
    /** 确认标记 1:待审核  2:审核通过  3:审核不通过 4:待完善 */
    @ApiModelProperty(value = "确认标记 1:待审核  2:审核通过  3:审核不通过 4:待完善", example = "")
    @TableField("confirm_flag")
    private Integer confirmFlag;
    /**
     * 确认意见
     */
    @ApiModelProperty(value = "确认意见")
    /** 审核确认时间 */
    @ApiModelProperty(value = "审核确认时间", example = "")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField("confirm_time")
    private Date confirmTime;
    /** 审核意见 */
    @ApiModelProperty(value = "审核意见", example = "")
    @TableField("confirm_notion")
    private String confirmNotion;
    /**
     * 创建人
     */
    @JsonSerialize(using = ToStringSerializer.class)
    @ApiModelProperty("创建人")
    @TableField(fill = FieldFill.INSERT)
    /** 创建人 */
    @ApiModelProperty(value = "创建人", example = "")
    @TableField("create_user")
    private Long createUser;
    /**
     * 创建时间
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("创建时间")
    @TableField(fill = FieldFill.INSERT)
    /** 创建时间 */
    @ApiModelProperty(value = "创建时间", example = "")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField("create_time")
    private Date createTime;
    /**
     * 更新人
     */
    @JsonSerialize(using = ToStringSerializer.class)
    @ApiModelProperty("更新人")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    /** 更新人 */
    @ApiModelProperty(value = "更新人", example = "")
    @TableField("update_user")
    private Long updateUser;
    /**
     * 更新时间
     */
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @ApiModelProperty("更新时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    /** 更新时间 */
    @ApiModelProperty(value = "更新时间", example = "")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField("update_time")
    private Date updateTime;
    /**
     * 是否删除
     */
    @TableLogic
    @ApiModelProperty("是否已删除 0:否  1:是")
    /** 是否删除 0:否  1:是 */
    @ApiModelProperty(value = "是否删除 0:否  1:是", example = "")
    @TableField("is_deleted")
    private Integer isDeleted;
    /** 法人现住地址 */
    @ApiModelProperty(value = "法人现住地址", example = "")
    @TableField("legal_temp_address")
    private String legalTempAddress;
    /** 法人年龄 */
    @ApiModelProperty(value = "法人年龄", example = "")
    @TableField("legal_age")
    private Integer legalAge;
    /** 法人性别 */
    @ApiModelProperty(value = "法人性别", example = "")
    @TableField("legal_gender")
    private Integer legalGender;
    /** 法人身份证 */
    @ApiModelProperty(value = "法人身份证", example = "")
    @TableField("legal_id_card")
    private String legalIdCard;
    /** 法人证件类型,业务字典  cardType */
    @ApiModelProperty(value = "法人证件类型,业务字典  cardType", example = "")
    @TableField("legal_card_type")
    private String legalCardType;
    /** 法人户籍地址 */
    @ApiModelProperty(value = "法人户籍地址", example = "")
    @TableField("legal_registered_address")
    private String legalRegisteredAddress;
    /** 法人岗位性质 */
    @ApiModelProperty(value = "法人岗位性质", example = "")
    @TableField("legal_job_nature")
    private String legalJobNature;
    /** 法人微信号 */
    @ApiModelProperty(value = "法人微信号", example = "")
    @TableField("legal_wx_account")
    private String legalWxAccount;
    /** 生日 */
    @ApiModelProperty(value = "生日", example = "")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField("birthday")
    private Date birthday;
    /** 民族 */
    @ApiModelProperty(value = "民族", example = "")
    @TableField("ethnicity")
    private Integer ethnicity;
    /** 工作单位(就职单位) */
    @ApiModelProperty(value = "工作单位(就职单位)", example = "")
    @TableField("employer")
    private String employer;
    /** 房东名字 */
    @ApiModelProperty(value = "房东名字", example = "")
    @TableField("landlord_name")
    private String landlordName;
    /** 房东手机号 */
    @ApiModelProperty(value = "房东手机号", example = "")
    @TableField("landlord_phone")
    private String landlordPhone;
    /** 房东微信号 */
    @ApiModelProperty(value = "房东微信号", example = "")
    @TableField("landlord_wx_account")
    private String landlordWxAccount;
    /** 房东身份证号 */
    @ApiModelProperty(value = "房东身份证号", example = "")
    @TableField("landlord_id_card")
    private String landlordIdCard;
}
src/main/java/org/springblade/modules/place/entity/PlacePractitionerEntity.java
@@ -16,9 +16,8 @@
 */
package org.springblade.modules.place.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
@@ -28,6 +27,7 @@
import org.springblade.core.tenant.mp.TenantEntity;
import java.io.Serializable;
import java.util.Date;
/**
 * 场所从业人员 实体类
@@ -48,25 +48,91 @@
    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private Long id;
    /**
     * 场所ID
     */
    @ApiModelProperty(value = "场所ID")
    /** 场所ID */
    @ApiModelProperty(value = "场所ID", example = "")
    @TableField("place_id")
    private Long placeId;
    /**
     * 姓名
     */
    @ApiModelProperty(value = "姓名")
    /** 姓名 */
    @ApiModelProperty(value = "姓名", example = "")
    @TableField("name")
    private String name;
    /**
     * 电话
     */
    @ApiModelProperty(value = "电话")
    /** 电话 */
    @ApiModelProperty(value = "电话", example = "")
    @TableField("telephone")
    private String telephone;
    /**
     * 暂住地
     */
    @ApiModelProperty(value = "暂住地")
    /** 现住地址 */
    @ApiModelProperty(value = "现住地址", example = "")
    @TableField("temp_address")
    private String tempAddress;
    /** 年龄 */
    @ApiModelProperty(value = "年龄", example = "")
    @TableField("age")
    private Integer age;
    /** 性别 */
    @ApiModelProperty(value = "性别", example = "")
    @TableField("gender")
    private Integer gender;
    /** 身份证 */
    @ApiModelProperty(value = "身份证", example = "")
    @TableField("id_card")
    private String idCard;
    /** 证件类型,业务字典  cardType */
    @ApiModelProperty(value = "证件类型,业务字典  cardType", example = "")
    @TableField("card_type")
    private String cardType;
    /** 工作单位(就职单位) */
    @ApiModelProperty(value = "工作单位(就职单位)", example = "")
    @TableField("employer")
    private String employer;
    /** 户籍地址 */
    @ApiModelProperty(value = "户籍地址", example = "")
    @TableField("registered_address")
    private String registeredAddress;
    /** 岗位性质 */
    @ApiModelProperty(value = "岗位性质", example = "")
    @TableField("job_nature")
    private String jobNature;
    /** 从业人员的照片 */
    @ApiModelProperty(value = "从业人员的照片", example = "")
    @TableField("employer_img")
    private String employerImg;
    /** 微信号 */
    @ApiModelProperty(value = "微信号", example = "")
    @TableField("wx_account")
    private String wxAccount;
    /** 创建时间 */
    @ApiModelProperty(value = "创建时间", example = "")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    @TableField(value = "create_time",fill = FieldFill.INSERT)
    private Date createTime;
    /** 生日 */
    @ApiModelProperty(value = "生日", example = "")
    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
    @TableField("birthday")
    private Date birthday;
    /** 民族 */
    @ApiModelProperty(value = "民族", example = "")
    @TableField("ethnicity")
    private Integer ethnicity;
    /** 民族 */
    @ApiModelProperty(value = "0:否1:是", example = "")
    @TableField("is_deleted")
    private Integer isDeleted;
}
src/main/java/org/springblade/modules/place/mapper/PlaceExtMapper.xml
@@ -194,5 +194,98 @@
          and jpe.place_id = #{placeExt.placeId}
    </select>
    <resultMap type="org.springblade.modules.place.dto.PlaceExtDTO" id="JczzPlaceExtDTOResult">
        <result property="id"    column="id"    />
        <result property="placeId"    column="place_id"    />
        <result property="imageUrls"    column="image_urls"    />
        <result property="legalPerson"    column="legal_person"    />
        <result property="legalTel"    column="legal_tel"    />
        <result property="planImageUrls"    column="plan_image_urls"    />
        <result property="confirmUserId"    column="confirm_user_id"    />
        <result property="confirmFlag"    column="confirm_flag"    />
        <result property="confirmTime"    column="confirm_time"    />
        <result property="confirmNotion"    column="confirm_notion"    />
        <result property="createUser"    column="create_user"    />
        <result property="createTime"    column="create_time"    />
        <result property="updateUser"    column="update_user"    />
        <result property="updateTime"    column="update_time"    />
        <result property="isDeleted"    column="is_deleted"    />
        <result property="legalTempAddress"    column="legal_temp_address"    />
        <result property="legalAge"    column="legal_age"    />
        <result property="legalGender"    column="legal_gender"    />
        <result property="legalIdCard"    column="legal_id_card"    />
        <result property="legalCardType"    column="legal_card_type"    />
        <result property="legalRegisteredAddress"    column="legal_registered_address"    />
        <result property="legalJobNature"    column="legal_job_nature"    />
        <result property="legalWxAccount"    column="legal_wx_account"    />
        <result property="birthday"    column="birthday"    />
        <result property="ethnicity"    column="ethnicity"    />
        <result property="employer"    column="employer"    />
    </resultMap>
    <sql id="selectJczzPlaceExt">
        select
            id,
            place_id,
            image_urls,
            legal_person,
            legal_tel,
            plan_image_urls,
            confirm_user_id,
            confirm_flag,
            confirm_time,
            confirm_notion,
            create_user,
            create_time,
            update_user,
            update_time,
            is_deleted,
            legal_temp_address,
            legal_age,
            legal_gender,
            legal_id_card,
            legal_card_type,
            legal_registered_address,
            legal_job_nature,
            legal_wx_account
        from
            jczz_place_ext
    </sql>
<!--    <select id="selectJczzPlaceExtById" parameterType="long" resultMap="JczzPlaceExtDTOResult">-->
<!--        <include refid="selectJczzPlaceExt"/>-->
<!--        where-->
<!--        id = #{id}-->
<!--    </select>-->
<!--    <select id="selectJczzPlaceExtList" parameterType="jczzplaceext.dto.JczzPlaceExtDTO" resultMap="JczzPlaceExtDTOResult">-->
<!--        <include refid="selectJczzPlaceExt"/>-->
<!--        <where>-->
<!--            <if test="id != null "> and id = #{id}</if>-->
<!--            <if test="placeId != null "> and place_id = #{placeId}</if>-->
<!--            <if test="imageUrls != null  and imageUrls != ''"> and image_urls = #{imageUrls}</if>-->
<!--            <if test="legalPerson != null  and legalPerson != ''"> and legal_person = #{legalPerson}</if>-->
<!--            <if test="legalTel != null  and legalTel != ''"> and legal_tel = #{legalTel}</if>-->
<!--            <if test="planImageUrls != null  and planImageUrls != ''"> and plan_image_urls = #{planImageUrls}</if>-->
<!--            <if test="confirmUserId != null "> and confirm_user_id = #{confirmUserId}</if>-->
<!--            <if test="confirmFlag != null "> and confirm_flag = #{confirmFlag}</if>-->
<!--            <if test="confirmTime != null "> and confirm_time = #{confirmTime}</if>-->
<!--            <if test="confirmNotion != null  and confirmNotion != ''"> and confirm_notion = #{confirmNotion}</if>-->
<!--            <if test="createUser != null "> and create_user = #{createUser}</if>-->
<!--            <if test="createTime != null "> and create_time = #{createTime}</if>-->
<!--            <if test="updateUser != null "> and update_user = #{updateUser}</if>-->
<!--            <if test="updateTime != null "> and update_time = #{updateTime}</if>-->
<!--            <if test="isDeleted != null "> and is_deleted = #{isDeleted}</if>-->
<!--            <if test="legalTempAddress != null  and legalTempAddress != ''"> and legal_temp_address = #{legalTempAddress}</if>-->
<!--            <if test="legalAge != null "> and legal_age = #{legalAge}</if>-->
<!--            <if test="legalGender != null "> and legal_gender = #{legalGender}</if>-->
<!--            <if test="legalIdCard != null  and legalIdCard != ''"> and legal_id_card = #{legalIdCard}</if>-->
<!--            <if test="legalCardType != null  and legalCardType != ''"> and legal_card_type = #{legalCardType}</if>-->
<!--            <if test="legalRegisteredAddress != null  and legalRegisteredAddress != ''"> and legal_registered_address = #{legalRegisteredAddress}</if>-->
<!--            <if test="legalJobNature != null  and legalJobNature != ''"> and legal_job_nature = #{legalJobNature}</if>-->
<!--            <if test="legalWxAccount != null  and legalWxAccount != ''"> and legal_wx_account = #{legalWxAccount}</if>-->
<!--        </where>-->
<!--    </select>-->
</mapper>
src/main/java/org/springblade/modules/place/mapper/PlaceMapper.xml
@@ -657,17 +657,20 @@
    <resultMap type="org.springblade.modules.place.dto.PlaceDTO" id="PlaceDTOResult">
        <result property="id"    column="id"    />
        <result property="houseCode"    column="house_code"    />
        <result property="houseCodeBinds"    column="house_code_binds"    />
        <result property="buildingCode"    column="building_code"    />
        <result property="principalUserId"    column="principal_user_id"    />
        <result property="principal"    column="principal"    />
        <result property="principalIdCard"    column="principal_id_card"    />
        <result property="principalPhone"    column="principal_phone"    />
        <result property="principalIdCard"    column="principal_id_card"    />
        <result property="placeName"    column="place_name"    />
        <result property="lng"    column="lng"    />
        <result property="lat"    column="lat"    />
        <result property="location"    column="location"    />
        <result property="imageUrls"    column="image_urls"    />
        <result property="gridId"    column="grid_id"    />
        <result property="gridCode"    column="grid_code"    />
        <result property="jwGridCode"    column="jw_grid_code"    />
        <result property="source"    column="source"    />
        <result property="status"    column="status"    />
        <result property="isScene"    column="is_scene"    />
src/main/java/org/springblade/modules/place/mapper/PlacePractitionerMapper.java
@@ -16,6 +16,7 @@
 */
package org.springblade.modules.place.mapper;
import liquibase.pro.packaged.S;
import org.apache.ibatis.annotations.Param;
import org.springblade.modules.place.entity.PlacePractitionerEntity;
import org.springblade.modules.place.vo.PlacePractitionerVO;
@@ -40,5 +41,12 @@
     */
    List<PlacePractitionerVO> selectPlacePractitionerPage(IPage page,@Param("placePractitioner") PlacePractitionerVO placePractitioner);
    /**
     * 统计数据
     * @param type
     * @return
     */
    Integer selectPlaceCountByType(@Param("type") Integer type);
}
src/main/java/org/springblade/modules/place/mapper/PlacePractitionerMapper.xml
@@ -4,10 +4,120 @@
    <!--自定义分页查询-->
    <select id="selectPlacePractitionerPage" resultType="org.springblade.modules.place.vo.PlacePractitionerVO">
        select * from jczz_place_practitioner where 1=1
        <if test="placePractitioner.placeId!=null">
            and place_id = #{placePractitioner.placeId}
        </if>
        SELECT
        *
        FROM
        jczz_place_practitioner jpp
        LEFT JOIN jczz_place jp ON jp.id = jpp.place_id
        LEFT JOIN jczz_place_poi_label jppl on jppl.place_id = jp.id and type = 3
        LEFT JOIN jczz_category jc on jc.category_no = jppl.poi_code
        <where>
            <if test="placePractitioner.id != null ">and jpp.id = #{placePractitioner.id}</if>
            <if test="placePractitioner.placeId != null ">and jpp.place_id = #{placePractitioner.placeId}</if>
            <if test="placePractitioner.name != null  and placePractitioner.name != ''">and jpp.name =
                #{placePractitioner.name}
            </if>
            <if test="placePractitioner.telephone != null  and placePractitioner.telephone != ''">and jpp.telephone =
                #{placePractitioner.telephone}
            </if>
            <if test="placePractitioner.tempAddress != null  and placePractitioner.tempAddress != ''">and jpp.temp_address =
                #{placePractitioner.tempAddress}
            </if>
            <if test="placePractitioner.age != null ">and jpp.age = #{placePractitioner.age}</if>
            <if test="placePractitioner.gender != null ">and jpp.gender = #{placePractitioner.gender}</if>
            <if test="placePractitioner.idCard != null  and placePractitioner.idCard != ''">and jpp.id_card =
                #{placePractitioner.idCard}
            </if>
            <if test="placePractitioner.cardType != null  and placePractitioner.cardType != ''">and jpp.card_type =
                #{placePractitioner.cardType}
            </if>
            <if test="placePractitioner.employer != null  and placePractitioner.employer != ''">and jpp.employer =
                #{placePractitioner.employer}
            </if>
            <if test="placePractitioner.registeredAddress != null  and placePractitioner.registeredAddress != ''">and
                jpp.registered_address = #{placePractitioner.registeredAddress}
            </if>
            <if test="placePractitioner.jobNature != null  and placePractitioner.jobNature != ''">and jpp.job_nature =
                #{placePractitioner.jobNature}
            </if>
            <if test="placePractitioner.employerImg != null  and placePractitioner.employerImg != ''">and jpp.employer_img =
                #{placePractitioner.employerImg}
            </if>
            <if test="placePractitioner.wxAccount != null  and placePractitioner.wxAccount != ''">and jpp.wx_account =
                #{placePractitioner.wxAccount}
            </if>
            <if test="placePractitioner.createTime != null ">and jpp.create_time = #{placePractitioner.createTime}</if>
            <if test="placePractitioner.birthday != null ">and jpp.birthday = #{placePractitioner.birthday}</if>
            <if test="placePractitioner.ethnicity != null ">and jpp.ethnicity = #{placePractitioner.ethnicity}</if>
            <if test="placePractitioner.type == 1">
                and (YEAR(CURDATE()) - YEAR(jpp.birthday)) &lt; 18
            </if>
            <if test="placePractitioner.type == 2">
                and jpp.ethnicity != 1
            </if>
            <if test="placePractitioner.type == 3">
                and (((YEAR(CURDATE()) - YEAR(jpp.birthday)) &lt; 18) or ( jpp.ethnicity != 1))
                and jc.category_no in (180202,180201)
            </if>
            and jpp.is_deleted = 0
        </where>
    </select>
    <resultMap type="org.springblade.modules.place.dto.PlacePractitionerDTO" id="JczzPlacePractitionerDTOResult">
        <result property="id" column="id"/>
        <result property="placeId" column="place_id"/>
        <result property="name" column="name"/>
        <result property="telephone" column="telephone"/>
        <result property="tempAddress" column="temp_address"/>
        <result property="age" column="age"/>
        <result property="gender" column="gender"/>
        <result property="idCard" column="id_card"/>
        <result property="cardType" column="card_type"/>
        <result property="employer" column="employer"/>
        <result property="registeredAddress" column="registered_address"/>
        <result property="jobNature" column="job_nature"/>
        <result property="employerImg" column="employer_img"/>
        <result property="wxAccount" column="wx_account"/>
        <result property="createTime" column="create_time"/>
    </resultMap>
    <sql id="selectJczzPlacePractitioner">
        select id,
               place_id,
               name,
               telephone,
               temp_address,
               age,
               gender,
               id_card,
               card_type,
               employer,
               registered_address,
               job_nature,
               employer_img,
               wx_account,
               create_time,
               birthday,
               ethnicity
        from jczz_place_practitioner
    </sql>
    <select id="selectPlaceCountByType" parameterType="long" resultType="integer">
        select
        count(1)
        from
        jczz_place_practitioner
        <where>
            <if test="type == 1">
                and (YEAR(CURDATE()) - YEAR(birthday)) &lt; 18
            </if>
            <if test="type == 2">
                and ethnicity != 1
            </if>
            and is_deleted = 0
        </where>
    </select>
src/main/java/org/springblade/modules/place/service/IPlacePractitionerService.java
@@ -40,4 +40,5 @@
    IPage<PlacePractitionerVO> selectPlacePractitionerPage(IPage<PlacePractitionerVO> page, PlacePractitionerVO placePractitioner);
    Object countByType();
}
src/main/java/org/springblade/modules/place/service/impl/PlacePractitionerServiceImpl.java
@@ -17,6 +17,7 @@
package org.springblade.modules.place.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springblade.common.constant.CommonConstant;
import org.springblade.modules.place.entity.PlacePractitionerEntity;
import org.springblade.modules.place.vo.PlacePractitionerVO;
import org.springblade.modules.place.mapper.PlacePractitionerMapper;
@@ -24,6 +25,9 @@
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.util.HashMap;
import java.util.Map;
/**
 * 场所从业人员 服务实现类
@@ -40,4 +44,19 @@
    }
    /**
     * 统计少数民族和未成年数量
     * @return
     */
    @Override
    public Object countByType() {
        // 未成年数量
        Integer minors = baseMapper.selectPlaceCountByType(CommonConstant.NUMBER_ONE);
        // 少数民族
        Integer nationalMinority = baseMapper.selectPlaceCountByType(CommonConstant.NUMBER_TWO);
        Map<String, Integer> resultMap = new HashMap<>();
        resultMap.put("minors",minors);
        resultMap.put("nationalMinority",nationalMinority);
        return resultMap;
    }
}
src/main/java/org/springblade/modules/place/vo/PlacePractitionerVO.java
@@ -16,6 +16,7 @@
 */
package org.springblade.modules.place.vo;
import io.swagger.annotations.ApiModelProperty;
import org.springblade.modules.place.entity.PlacePractitionerEntity;
import org.springblade.core.tool.node.INode;
import lombok.Data;
@@ -32,4 +33,7 @@
public class PlacePractitionerVO extends PlacePractitionerEntity {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(value = "类型1:未成年 2:少数民族 3:包含1和2", example = "")
    private Integer type;
}