From d52c20ed07bcea2d848bdab25f2c49c21c00d269 Mon Sep 17 00:00:00 2001 From: liurunyu <lry9898@163.com> Date: 星期二, 27 二月 2024 12:56:22 +0800 Subject: [PATCH] Merge branch 'master' of http://8.140.179.55:20000/r/pipIrr-SV --- pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PayInfo.java | 157 ++++++ pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/PipIrrWebChatApplication.java | 2 pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PaymentSv.java | 35 + pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/RestTemplateUtil.java | 102 ++++ pipIrr-platform/pipIrr-web/pipIrr-web-base/src/main/java/com/dy/pipIrrBase/user/QueryVo.java | 2 pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PayHelper.java | 119 ++++ pipIrr-platform/pipIrr-web/pipIrr-web-webchat/pom.xml | 33 + pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/RestTemplateConfig.java | 81 +++ pipIrr-platform/pipIrr-global/src/main/resources/mapper/PrIntakeMapper.xml | 18 pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeWebchatLogonStateMapper.xml | 91 +++ pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/RestTemplateWechatCertConfig.java | 57 ++ pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PaymentCtrl.java | 258 ++++++++++ pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/dto/Code2Session.java | 39 + pipIrr-platform/pipIrr-web/pipIrr-web-base/src/main/java/com/dy/pipIrrBase/user/UserCtrl.java | 17 pipIrr-platform/pipIrr-common/src/main/java/com/dy/common/mybatis/AutoGenerateIdInterceptor.java | 20 pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/ICallback.java | 15 pipIrr-platform/pipIrr-global/src/main/resources/mapper/BaUserMapper.xml | 8 pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/OkHttpUtil.java | 239 +++++++++ pipIrr-platform/pipIrr-web/pipIrr-web-base/src/main/java/com/dy/pipIrrBase/user/UserSv.java | 6 pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeWebchatLogonState.java | 60 ++ pipIrr-platform/pipIrr-web/pipIrr-web-project/src/main/java/com/dy/pipIrrProject/flowMonitoring/FlowMonitoringCtrl.java | 4 pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/resources/application.yml | 4 pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoSe/SeWebchatLogonStateMapper.java | 27 + 23 files changed, 1,361 insertions(+), 33 deletions(-) diff --git a/pipIrr-platform/pipIrr-common/src/main/java/com/dy/common/mybatis/AutoGenerateIdInterceptor.java b/pipIrr-platform/pipIrr-common/src/main/java/com/dy/common/mybatis/AutoGenerateIdInterceptor.java index eca91c8..6265e27 100644 --- a/pipIrr-platform/pipIrr-common/src/main/java/com/dy/common/mybatis/AutoGenerateIdInterceptor.java +++ b/pipIrr-platform/pipIrr-common/src/main/java/com/dy/common/mybatis/AutoGenerateIdInterceptor.java @@ -1,16 +1,14 @@ package com.dy.common.mybatis; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSONWriter; import com.dy.common.po.BaseEntity; import com.dy.common.util.IDLongGenerator; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; -import org.apache.ibatis.plugin.Interceptor; -import org.apache.ibatis.plugin.Intercepts; -import org.apache.ibatis.plugin.Invocation; -import org.apache.ibatis.plugin.Plugin; -import org.apache.ibatis.plugin.Signature; +import org.apache.ibatis.plugin.*; import java.lang.reflect.Method; @@ -28,6 +26,7 @@ static int MAPPED_STATEMENT_INDEX = 0; static int PARAMETER_INDEX = 1; static String BASE_FIELD_SET_PRIMARY_KEY_FUNTION_NAME = "setId"; + static String BASE_FIELD_SET_PRIMARY_KEY_FUNTION_NAME2 = "setUserId"; /** * 鎷︽埅閫昏緫瀹炵幇 @@ -45,8 +44,15 @@ Class<?> entityClass = entity.getClass(); Method setMt = null ; try{ - //鏈変竴浜涘疄浣撴病鏈塱d锛屼緥濡備腑闂磋〃 - setMt = entityClass.getMethod(BASE_FIELD_SET_PRIMARY_KEY_FUNTION_NAME, Long.class) ; + // 娣诲姞鐢ㄦ埛鏃秛serId涓鸿嚜鍔ㄧ敓鎴愪富閿�俽oleId瀛樺湪鏃舵槸涓虹敤鎴风粦瑙掕壊 + String jsonString = JSONObject.toJSONString(entity, JSONWriter.Feature.WriteMapNullValue); + JSONObject jsonObject = JSONObject.parseObject(jsonString); + if(jsonObject.containsKey("userId") && !jsonObject.containsKey("roleId")) { + setMt = entityClass.getMethod(BASE_FIELD_SET_PRIMARY_KEY_FUNTION_NAME2, Long.class) ; + }else { + //鏈変竴浜涘疄浣撴病鏈塱d锛屼緥濡備腑闂磋〃 + setMt = entityClass.getMethod(BASE_FIELD_SET_PRIMARY_KEY_FUNTION_NAME, Long.class) ; + } }catch (Exception e){ //褰揺ntityClass娌℃湁setId鏂规硶鏃讹紝浼氭姏鍑哄紓甯� } diff --git a/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoSe/SeWebchatLogonStateMapper.java b/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoSe/SeWebchatLogonStateMapper.java new file mode 100644 index 0000000..4837b8f --- /dev/null +++ b/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/daoSe/SeWebchatLogonStateMapper.java @@ -0,0 +1,27 @@ +package com.dy.pipIrrGlobal.daoSe; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.dy.pipIrrGlobal.pojoSe.SeWebchatLogonState; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author ZhuBaoMin + * @date 2024-02-22 17:31 + * @LastEditTime 2024-02-22 17:31 + * @Description + */ + +@Mapper +public interface SeWebchatLogonStateMapper extends BaseMapper<SeWebchatLogonState> { + int deleteByPrimaryKey(Long id); + + int insert(SeWebchatLogonState record); + + int insertSelective(SeWebchatLogonState record); + + SeWebchatLogonState selectByPrimaryKey(Long id); + + int updateByPrimaryKeySelective(SeWebchatLogonState record); + + int updateByPrimaryKey(SeWebchatLogonState record); +} \ No newline at end of file diff --git a/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeWebchatLogonState.java b/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeWebchatLogonState.java new file mode 100644 index 0000000..b8c01be --- /dev/null +++ b/pipIrr-platform/pipIrr-global/src/main/java/com/dy/pipIrrGlobal/pojoSe/SeWebchatLogonState.java @@ -0,0 +1,60 @@ +package com.dy.pipIrrGlobal.pojoSe; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.alibaba.fastjson2.writer.ObjectWriterImplToString; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.dy.common.po.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.*; + +import java.util.Date; + +/** + * @author ZhuBaoMin + * @date 2024-02-22 17:31 + * @LastEditTime 2024-02-22 17:31 + * @Description + */ + +@TableName(value="se_webchat_logon_state", autoResultMap = true) +@Data +@Builder +@ToString +@NoArgsConstructor +@AllArgsConstructor +@Schema(name = "鐧诲綍鎬佽褰曞疄浣�") +public class SeWebchatLogonState implements BaseEntity { + public static final long serialVersionUID = 202402221735001L; + + /** + * 涓婚敭 + */ + @JSONField(serializeUsing= ObjectWriterImplToString.class) + @TableId(type = IdType.INPUT) + @Schema(description = "瀹炰綋id", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + private Long id; + + /** + * 鐢ㄦ埛鍞竴鏍囪瘑 + */ + @Schema(description = "鐢ㄦ埛鍞竴鏍囪瘑", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @NotBlank(message = "鐢ㄦ埛鍞竴鏍囪瘑涓嶈兘涓虹┖") + private String openId; + + /** + * 浼氳瘽瀵嗛挜 + */ + @Schema(description = "浼氳瘽瀵嗛挜", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @NotBlank(message = "浼氳瘽瀵嗛挜涓嶈兘涓虹┖") + private String sessionKey; + + /** + * 鍒涘缓鏃堕棿 + */ + @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @NotBlank(message = "鍒涘缓鏃堕棿涓嶈兘涓虹┖") + private Date createTime; +} \ No newline at end of file diff --git a/pipIrr-platform/pipIrr-global/src/main/resources/mapper/BaUserMapper.xml b/pipIrr-platform/pipIrr-global/src/main/resources/mapper/BaUserMapper.xml index ccf499f..f99beee 100644 --- a/pipIrr-platform/pipIrr-global/src/main/resources/mapper/BaUserMapper.xml +++ b/pipIrr-platform/pipIrr-global/src/main/resources/mapper/BaUserMapper.xml @@ -271,8 +271,8 @@ AND user.supperAdmin != 1 AND user.deleted = 0 - <if test = "name != null and name !=''"> - AND user.name like CONCAT('%',#{name},'%') + <if test = "userName != null and userName !=''"> + AND user.name like CONCAT('%',#{userName},'%') </if> <if test = "phone != null and phone !=''"> @@ -304,8 +304,8 @@ AND user.supperAdmin != 1 AND user.deleted = 0 - <if test = "name != null and name !=''"> - AND user.name like CONCAT('%',#{name},'%') + <if test = "userName != null and userName !=''"> + AND user.name like CONCAT('%',#{userName},'%') </if> <if test = "phone != null and phone !=''"> diff --git a/pipIrr-platform/pipIrr-global/src/main/resources/mapper/PrIntakeMapper.xml b/pipIrr-platform/pipIrr-global/src/main/resources/mapper/PrIntakeMapper.xml index 85772ad..8470ae2 100644 --- a/pipIrr-platform/pipIrr-global/src/main/resources/mapper/PrIntakeMapper.xml +++ b/pipIrr-platform/pipIrr-global/src/main/resources/mapper/PrIntakeMapper.xml @@ -216,9 +216,9 @@ FROM pr_intake ge INNER JOIN pr_divide divi ON ge.divideId = divi.id INNER JOIN ba_block blo ON divi.blockId = blo.id - INNER JOIN ba_district country ON ge.countyId = country.id - INNER JOIN ba_district town ON ge.townId = town.id - INNER JOIN ba_district village ON ge.villageId = village.id + LEFT JOIN ba_district country ON ge.countyId = country.id + LEFT JOIN ba_district town ON ge.townId = town.id + LEFT JOIN ba_district village ON ge.villageId = village.id <where> ge.deleted = 0 AND divi.deleted = 0 @@ -232,7 +232,7 @@ </if> <if test="blockName != null and blockName != ''"> - AND blo.name = #{blockName} + AND blo.name LIKE CONCAT('%', #{blockName}, '%') </if> <if test="isBinded == 0"> @@ -265,9 +265,9 @@ FROM pr_intake ge INNER JOIN pr_divide divi ON ge.divideId = divi.id INNER JOIN ba_block blo ON divi.blockId = blo.id - INNER JOIN ba_district country ON ge.countyId = country.id - INNER JOIN ba_district town ON ge.townId = town.id - INNER JOIN ba_district village ON ge.villageId = village.id + LEFT JOIN ba_district country ON ge.countyId = country.id + LEFT JOIN ba_district town ON ge.townId = town.id + LEFT JOIN ba_district village ON ge.villageId = village.id <where> ge.deleted = 0 AND divi.deleted = 0 @@ -281,7 +281,7 @@ </if> <if test="blockName != null and blockName != ''"> - AND blo.name = #{blockName} + AND blo.name LIKE CONCAT('%', #{blockName}, '%') </if> <if test="isBinded == 0"> @@ -299,7 +299,7 @@ </where> ORDER BY ge.operateDt DESC <if test="pageCurr != null and pageSize != null"> - LIMIT ${pageCurr}, ${pageSize} + LIMIT ${(pageCurr-1)*pageSize}, ${pageSize} </if> </select> diff --git a/pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeWebchatLogonStateMapper.xml b/pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeWebchatLogonStateMapper.xml new file mode 100644 index 0000000..973ab13 --- /dev/null +++ b/pipIrr-platform/pipIrr-global/src/main/resources/mapper/SeWebchatLogonStateMapper.xml @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.dy.pipIrrGlobal.daoSe.SeWebchatLogonStateMapper"> + <resultMap id="BaseResultMap" type="com.dy.pipIrrGlobal.pojoSe.SeWebchatLogonState"> + <!--@mbg.generated--> + <!--@Table se_webchat_logon_state--> + <id column="id" jdbcType="BIGINT" property="id" /> + <result column="open_id" jdbcType="VARCHAR" property="openId" /> + <result column="session_key" jdbcType="VARCHAR" property="sessionKey" /> + <result column="create_time" jdbcType="TIMESTAMP" property="createTime" /> + </resultMap> + <sql id="Base_Column_List"> + <!--@mbg.generated--> + id, open_id, session_key, create_time + </sql> + <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap"> + <!--@mbg.generated--> + select + <include refid="Base_Column_List" /> + from se_webchat_logon_state + where id = #{id,jdbcType=BIGINT} + </select> + <delete id="deleteByPrimaryKey" parameterType="java.lang.Long"> + <!--@mbg.generated--> + delete from se_webchat_logon_state + where id = #{id,jdbcType=BIGINT} + </delete> + <insert id="insert" parameterType="com.dy.pipIrrGlobal.pojoSe.SeWebchatLogonState"> + <!--@mbg.generated--> + insert into se_webchat_logon_state (id, open_id, session_key, + create_time) + values (#{id,jdbcType=BIGINT}, #{openId,jdbcType=VARCHAR}, #{sessionKey,jdbcType=VARCHAR}, + #{createTime,jdbcType=TIMESTAMP}) + </insert> + <insert id="insertSelective" parameterType="com.dy.pipIrrGlobal.pojoSe.SeWebchatLogonState"> + <!--@mbg.generated--> + insert into se_webchat_logon_state + <trim prefix="(" suffix=")" suffixOverrides=","> + <if test="id != null"> + id, + </if> + <if test="openId != null"> + open_id, + </if> + <if test="sessionKey != null"> + session_key, + </if> + <if test="createTime != null"> + create_time, + </if> + </trim> + <trim prefix="values (" suffix=")" suffixOverrides=","> + <if test="id != null"> + #{id,jdbcType=BIGINT}, + </if> + <if test="openId != null"> + #{openId,jdbcType=VARCHAR}, + </if> + <if test="sessionKey != null"> + #{sessionKey,jdbcType=VARCHAR}, + </if> + <if test="createTime != null"> + #{createTime,jdbcType=TIMESTAMP}, + </if> + </trim> + </insert> + <update id="updateByPrimaryKeySelective" parameterType="com.dy.pipIrrGlobal.pojoSe.SeWebchatLogonState"> + <!--@mbg.generated--> + update se_webchat_logon_state + <set> + <if test="openId != null"> + open_id = #{openId,jdbcType=VARCHAR}, + </if> + <if test="sessionKey != null"> + session_key = #{sessionKey,jdbcType=VARCHAR}, + </if> + <if test="createTime != null"> + create_time = #{createTime,jdbcType=TIMESTAMP}, + </if> + </set> + where id = #{id,jdbcType=BIGINT} + </update> + <update id="updateByPrimaryKey" parameterType="com.dy.pipIrrGlobal.pojoSe.SeWebchatLogonState"> + <!--@mbg.generated--> + update se_webchat_logon_state + set open_id = #{openId,jdbcType=VARCHAR}, + session_key = #{sessionKey,jdbcType=VARCHAR}, + create_time = #{createTime,jdbcType=TIMESTAMP} + where id = #{id,jdbcType=BIGINT} + </update> +</mapper> \ No newline at end of file diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-base/src/main/java/com/dy/pipIrrBase/user/QueryVo.java b/pipIrr-platform/pipIrr-web/pipIrr-web-base/src/main/java/com/dy/pipIrrBase/user/QueryVo.java index 4fc9799..a04ac59 100644 --- a/pipIrr-platform/pipIrr-web/pipIrr-web-base/src/main/java/com/dy/pipIrrBase/user/QueryVo.java +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-base/src/main/java/com/dy/pipIrrBase/user/QueryVo.java @@ -15,7 +15,7 @@ public class QueryVo extends QueryConditionVo { @Schema(description = "鐢ㄦ埛濮撳悕") - public String name; + public String userName; @Schema(description = "鐢ㄦ埛濮撳悕鐢佃瘽") public String phone; diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-base/src/main/java/com/dy/pipIrrBase/user/UserCtrl.java b/pipIrr-platform/pipIrr-web/pipIrr-web-base/src/main/java/com/dy/pipIrrBase/user/UserCtrl.java index 9d58bd9..3604402 100644 --- a/pipIrr-platform/pipIrr-web/pipIrr-web-base/src/main/java/com/dy/pipIrrBase/user/UserCtrl.java +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-base/src/main/java/com/dy/pipIrrBase/user/UserCtrl.java @@ -89,11 +89,18 @@ ) }) @GetMapping(path = "some") + //@PostMapping(path = "some", consumes = MediaType.APPLICATION_JSON_VALUE) @SsoAop() public BaseResponse<QueryResultVo<List<VoUserInfo>>> some(QueryVo vo) { try { QueryResultVo<List<VoUserInfo>> res = this.sv.selectSome(vo); - return BaseResponseUtils.buildSuccess(res); + if(res.itemTotal == 0) { + return BaseResponseUtils.buildFail(SystemResultCode.THE_USER_NOT_EXIST.getMessage()); + }else { + return BaseResponseUtils.buildSuccess(res); + } + + } catch (Exception e) { log.error("鏌ヨ鐢ㄦ埛寮傚父", e); return BaseResponseUtils.buildException(e.getMessage()); @@ -143,7 +150,7 @@ return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); } po.userId = null; - int count; + Long userId; try { po.supperAdmin = Constant.no.byteValue() ; po.disabled = Disabled.NO;//榛樿涓嶇鐢� @@ -158,13 +165,13 @@ } else { po.password = MD5.encrypt(defaultPassword); } - count = this.sv.save(po); - this.sv.setRoles(po.userId, po.roleIds); + userId = this.sv.save(po); + this.sv.setRoles(userId, po.roleIds); } catch (Exception e) { log.error("淇濆瓨鐢ㄦ埛寮傚父", e); return BaseResponseUtils.buildException(e.getMessage()); } - if (count <= 0) { + if (userId <= 0) { return BaseResponseUtils.buildFail("鏁版嵁搴撳瓨鍌ㄥけ璐�"); } else { return BaseResponseUtils.buildSuccess(true); diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-base/src/main/java/com/dy/pipIrrBase/user/UserSv.java b/pipIrr-platform/pipIrr-web/pipIrr-web-base/src/main/java/com/dy/pipIrrBase/user/UserSv.java index 6ff9209..9dce98e 100644 --- a/pipIrr-platform/pipIrr-web/pipIrr-web-base/src/main/java/com/dy/pipIrrBase/user/UserSv.java +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-base/src/main/java/com/dy/pipIrrBase/user/UserSv.java @@ -113,8 +113,10 @@ * @return 褰卞搷璁板綍鏁伴噺 */ @Transactional - public int save(BaUser po){ - return this.dao.putin(po) ; + public Long save(BaUser po){ + //return this.dao.putin(po) ; + this.dao.putin(po) ; + return po.getUserId(); } /** diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-project/src/main/java/com/dy/pipIrrProject/flowMonitoring/FlowMonitoringCtrl.java b/pipIrr-platform/pipIrr-web/pipIrr-web-project/src/main/java/com/dy/pipIrrProject/flowMonitoring/FlowMonitoringCtrl.java index a5399b5..c30ad63 100644 --- a/pipIrr-platform/pipIrr-web/pipIrr-web-project/src/main/java/com/dy/pipIrrProject/flowMonitoring/FlowMonitoringCtrl.java +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-project/src/main/java/com/dy/pipIrrProject/flowMonitoring/FlowMonitoringCtrl.java @@ -228,9 +228,9 @@ schema = @Schema(implementation = PrFlowMonitoring.class))} ) }) - @GetMapping(path = "getFlowMonitoring", consumes = MediaType.APPLICATION_JSON_VALUE) + @GetMapping(path = "getFlowMonitoring") @SsoAop() - public BaseResponse<QueryResultVo<List<VoFlowMonitoring>>> getFlowMonitoring(@RequestBody @Parameter(description = "鏌ヨform琛ㄥ崟json鏁版嵁", required = true) QueryVo vo){ + public BaseResponse<QueryResultVo<List<VoFlowMonitoring>>> getFlowMonitoring(QueryVo vo){ try { QueryResultVo<List<VoFlowMonitoring>> res = flowMonitoringSv.getPrFlowMonitorings(vo); if(res == null) { diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/pom.xml b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/pom.xml index 0bd2b52..5cb8ab3 100644 --- a/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/pom.xml +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/pom.xml @@ -16,6 +16,39 @@ <name>pipIrr-web-webchat</name> <description>web寰俊鏀粯妯″潡</description> + <dependencies> + <!--寰俊鏀粯--> + <dependency> + <groupId>com.github.wechatpay-apiv3</groupId> + <artifactId>wechatpay-java</artifactId> + <version>0.2.12</version> + </dependency> + + <!--OkHttp--> + <dependency> + <groupId>com.squareup.okhttp3</groupId> + <artifactId>okhttp</artifactId> + <version>4.9.2</version> + </dependency> + + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpclient</artifactId> + <version>4.5.3</version> + </dependency> + + <dependency> + <groupId>org.apache.httpcomponents.client5</groupId> + <artifactId>httpclient5</artifactId> + <version>5.1.3</version> + </dependency> + <dependency> + <groupId>org.apache.httpcomponents.client5</groupId> + <artifactId>httpclient5-fluent</artifactId> + <version>5.1.3</version> + </dependency> + + </dependencies> <build> <plugins> <!-- 鐢熸垚涓嶅寘鍚緷璧杍ar鐨勫彲鎵цjar鍖� diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/PipIrrWebChatApplication.java b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/PipIrrWebChatApplication.java index 825a383..2517c4a 100644 --- a/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/PipIrrWebChatApplication.java +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/PipIrrWebChatApplication.java @@ -18,7 +18,7 @@ @EnableAspectJAutoProxy @EnableMultiDataSource @ComponentScan(basePackages = {"com.dy.common", "com.dy.pipIrrGlobal", "com.dy.pipirrWebChat"}) -@MapperScan({"com.dy.pipIrrGlobal.daoPr", "com.dy.pipIrrGlobal.daoBa"}) +@MapperScan({"com.dy.pipIrrGlobal.daoSe", "com.dy.pipIrrGlobal.daoBa"}) public class PipIrrWebChatApplication { public static void main(String[] args) { diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/RestTemplateConfig.java b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/RestTemplateConfig.java new file mode 100644 index 0000000..0cbc9c1 --- /dev/null +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/RestTemplateConfig.java @@ -0,0 +1,81 @@ +package com.dy.pipirrWebChat.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.web.client.RestTemplate; + +import java.nio.charset.StandardCharsets; +import java.util.List; +//import org.apache.http.client.HttpClient; + +/** + * @author ZhuBaoMin + * @date 2024-02-23 15:42 + * @LastEditTime 2024-02-23 15:42 + * @Description + */ + +@Configuration +public class RestTemplateConfig { + @Autowired + private RestTemplateWechatCertConfig restTemplateWechatCertConfig; + + //@Bean + //public RestTemplate restTemplate() { + // return new RestTemplate(); + //} + + //@Bean + //public RestTemplate restTemplate() { + // String mchid = PayInfo.mchid; + // RestTemplate restTemplate = null; + // try { + // KeyStore keyStore = KeyStore.getInstance("PKCS12"); + // //InputStream cp = this.getClass().getResourceAsStream("apiclient_cert.p12"); + // FileInputStream instream = new FileInputStream(new File("C:\\webchat\\apiclient_cert.p12")); + // keyStore.load(instream, mchid.toCharArray()); + // // Trust own CA and all self-signed certs + // SSLContext sslcontext = SSLContextBuilder.create() + // .loadKeyMaterial(keyStore, mchid.toCharArray()) + // .build(); + // //Allow TLSv1 protocol only + // SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, NoopHostnameVerifier.INSTANCE); + // CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); + // HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); + // + // restTemplate = new RestTemplate(factory); + // //灏嗚浆鎹㈠櫒鐨勭紪鐮佹崲鎴恥tf-8 + // restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(Charset.forName("utf-8"))); + // //System.out.println("restTemplate.hashCode():" + restTemplate.hashCode()); + // } catch (Exception e) { + // e.printStackTrace(); + // } + // return restTemplate; + //} + + //@Bean(name = "wechatRestTemplate") + @Bean() + public RestTemplate restTemplate() throws Exception { + RestTemplate restTemplate = new RestTemplate(restTemplateWechatCertConfig.wechatHttpRequestFactory()); + // 娣诲姞鎷︽埅鍣� + //List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(); + //RestTemplateWechatCertConfig.MyRequestInterceptor myRequestInterceptor = new RestTemplateWechatCertConfig.MyRequestInterceptor(); + //interceptors.add(myRequestInterceptor); + //restTemplate.setInterceptors(interceptors); + + // 涓枃涔辩爜锛屼富瑕佹槸 StringHttpMessageConverter鐨勯粯璁ょ紪鐮佷负ISO瀵艰嚧鐨� + List<HttpMessageConverter<?>> list = restTemplate.getMessageConverters(); + for (HttpMessageConverter converter : list) { + if (converter instanceof StringHttpMessageConverter) { + ((StringHttpMessageConverter) converter).setDefaultCharset(StandardCharsets.UTF_8); + break; + } + } + + return restTemplate; + } +} + diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/RestTemplateWechatCertConfig.java b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/RestTemplateWechatCertConfig.java new file mode 100644 index 0000000..f566a74 --- /dev/null +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/config/RestTemplateWechatCertConfig.java @@ -0,0 +1,57 @@ +package com.dy.pipirrWebChat.config; + +import com.dy.pipirrWebChat.payment.PayInfo; +import okhttp3.OkHttpClient; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; +import java.io.File; +import java.io.FileInputStream; +import java.security.KeyStore; + +/** + * @author ZhuBaoMin + * @date 2024-02-23 19:18 + * @LastEditTime 2024-02-23 19:18 + * @Description + */ + +@Configuration +public class RestTemplateWechatCertConfig { + String mchid = PayInfo.mchid; + + @Bean + @ConfigurationProperties(prefix = "org.liurb.core.rest-template.config.connection") + public ClientHttpRequestFactory wechatHttpRequestFactory() throws Exception { + + KeyStore keyStore = KeyStore.getInstance("PKCS12"); + //InputStream cp = this.getClass().getResourceAsStream("apiclient_cert.p12"); + FileInputStream instream = new FileInputStream(new File("C:\\webchat\\apiclient_cert.p12")); + keyStore.load(instream, mchid.toCharArray()); + + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + keyManagerFactory.init(keyStore, mchid.toCharArray()); + + SSLContext context = SSLContext.getInstance("TLS"); + context.init(keyManagerFactory.getKeyManagers(), null, null); + + OkHttpClient okHttpClient = new OkHttpClient.Builder() + .sslSocketFactory(context.getSocketFactory(), getDefaultX509TrustManager()) + .build(); + + return new OkHttp3ClientHttpRequestFactory(okHttpClient); + } + + private static X509TrustManager getDefaultX509TrustManager() throws Exception { + TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + factory.init((KeyStore) null); + return (X509TrustManager) factory.getTrustManagers()[0]; + } +} \ No newline at end of file diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PayHelper.java b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PayHelper.java new file mode 100644 index 0000000..9412ba3 --- /dev/null +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PayHelper.java @@ -0,0 +1,119 @@ +package com.dy.pipirrWebChat.payment; + +import javax.crypto.NoSuchPaddingException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; +import java.util.Random; + +/** + * @author ZhuBaoMin + * @date 2024-02-22 20:31 + * @LastEditTime 2024-02-22 20:31 + * @Description + */ +public class PayHelper { + private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + /** + * 鑾峰彇32浣嶉殢鏈哄瓧绗︿覆 + * @return 闅忔満涓� + */ + public static String generateRandomString() { + Random random = new Random(); + StringBuilder sb = new StringBuilder(32); + for (int i = 0; i < 32; i++) { + int index = random.nextInt(CHARACTERS.length()); + sb.append(CHARACTERS.charAt(index)); + } + return sb.toString(); + } + + /** + * 鑾峰彇绉侀挜瀵硅薄 + * @param filename 绉侀挜鏂囦欢璺緞 + * @return 绉侀挜瀵硅薄 + * @throws IOException + */ + public static PrivateKey getPrivateKey(String filename) throws IOException { + //String filename = "C:\\webchat\\apiclient_key.pem"; + String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8"); + try { + String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", "") + .replaceAll("\\s+", ""); + KeyFactory kf = KeyFactory.getInstance("RSA"); + return kf.generatePrivate( + new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey))); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("褰撳墠Java鐜涓嶆敮鎸丷SA", e); + } catch (InvalidKeySpecException e) { + throw new RuntimeException("鏃犳晥鐨勫瘑閽ユ牸寮�"); + } + } + + /** + * 鏋勯�犵鍚嶄覆_涓嬪崟 + * @param method HTTP璇锋眰鏂规硶 + * @param url URL + * @param timestamp 鏃堕棿鎴� + * @param nonceStr 闅忔満涓� + * @param body 鎶ユ枃涓婚 + * @return 绛惧悕涓� + */ + public static String buildMessage_order(String method, String url, long timestamp, String nonceStr, String body) { + return method + "\n" + + url + "\n" + + timestamp + "\n" + + nonceStr + "\n" + + body + "\n"; + } + + public static String buildMessage_signAgain(String appid, String timestamp, String nonceStr, String pkg) { + return appid + "\n" + + timestamp + "\n" + + nonceStr + "\n" + + pkg + "\n"; + } + + /** + * 绛惧悕 + * @param message 琚鍚嶄俊鎭� + * @param certFileName 绉侀挜璇佷功鏂囦欢璺緞 + * @return signature绛惧悕鍊硷紝绛惧悕淇℃伅涓殑涓�椤癸紝鍙備笌鐢熸垚绛惧悕淇℃伅 + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + * @throws SignatureException + * @throws IOException + */ + public static String sign(byte[] message, String certFileName) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException { + Signature sign = Signature.getInstance("SHA256withRSA"); + sign.initSign(getPrivateKey(certFileName)); + sign.update(message); + return Base64.getEncoder().encodeToString(sign.sign()); + } + + /** + * 鑾峰彇绛惧悕淇℃伅 + * @param method + * @param url + * @param body + * @return 绛惧悕淇℃伅锛孒TTP澶翠腑鐨勭鍚嶄俊鎭� + * HTTP澶达細Authorization: 璁よ瘉绫诲瀷 绛惧悕淇℃伅 + * 璁よ瘉绫诲瀷锛學ECHATPAY2-SHA256-RSA2048 + */ + public static String getToken(String method, String url, String body, String nonceStr, Long timestamp, String certFileName) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException, NoSuchPaddingException { + String message = buildMessage_order(method, url, timestamp, nonceStr, body); + String signature = sign(message.getBytes("utf-8"), certFileName); + + return "mchid=\"" + PayInfo.mchid + "\"," + + "nonce_str=\"" + nonceStr + "\"," + + "timestamp=\"" + timestamp + "\"," + + "serial_no=\"" + PayInfo.serial_no + "\"," + + "signature=\"" + signature + "\""; + } +} diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PayInfo.java b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PayInfo.java new file mode 100644 index 0000000..ef11dac --- /dev/null +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PayInfo.java @@ -0,0 +1,157 @@ +package com.dy.pipirrWebChat.payment; + +/** + * @author ZhuBaoMin + * @date 2024-02-22 20:20 + * @LastEditTime 2024-02-22 20:20 + * @Description + */ +public class PayInfo { + /* + * 灏忕▼搴忕櫥褰旳PI + */ + public static String loginUrl = "https://api.weixin.qq.com/sns/jscode2session"; + + /* + * 缁熶竴涓嬪崟API + */ + //public static String orderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder"; + public static String orderUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; + + /** + * 骞冲彴璇佷功涓嬭浇URL + */ + public static String certificates = "https://api.mch.weixin.qq.com/v3/certificates"; + + /* + * 鏀粯缁撴灉閫氱煡API + */ + public static String notifyUrl = "https://www.muxiaobao.com/api/Payment/OrderNotify"; + + /* + * 鏌ヨ璁㈠崟API + */ + public static String queryUrl = "https://api.mch.weixin.qq.com/pay/orderquery"; + + /* + * 鐢宠閫�娆続PI + */ + public static String refundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund"; + + /* + * 閫�娆鹃�氱煡API + */ + public static String refundNotifyUrl = "https://www.muxiaobao.com/wxpay/pay.action"; + + /* + * 閫�娆炬煡璇PI + */ + public static String refundQueryUrl = "https://api.mch.weixin.qq.com/pay/refundquery"; + + /* + * 灏忕▼搴忓敮涓�鏍囪瘑 + */ + public static String appid = "wxf776aebf73f34962"; + + /* + * 灏忕▼搴忕殑 app secret + */ + public static String secret = "b05862b7fa8a944114dbe970c2764ae6"; + + /* + * 灏忕▼搴忕殑鎺堟潈绫诲瀷锛岀櫥褰曞嚟璇佹牎楠屼娇鐢� + */ + public static String grantType = "authorization_code"; + + /* + * 鍟嗘埛鍙�(寰俊鏀粯鍒嗛厤鐨勫晢鎴峰彿) + */ + public static String mchid = "1640721520"; + + /* + * 鍟嗘埛骞冲彴璁剧疆鐨勫瘑閽ey + */ + public static String key = "DaYuJieShuiYanJiuYuan20230412ABC"; + + /** + * 鍟嗘埛API璇佷功搴忓垪鍙� + */ + public static String serial_no = "52D65AA66405C738670377F467178F4C950E1606"; + + /* + * 缁堢IP锛岃皟鐢ㄥ井淇℃敮浠楢PI鐨勬満鍣↖P + */ + public static String addrIp = "47.104.211.89"; + + /* + * 闅忔満瀛楃涓诧紝闀垮害瑕佹眰鍦�32浣嶄互鍐� + */ + //public static String nonceStr = PayHelper.generateRandomString(); + + /* + * 鏃堕棿鎴� 浠�1970骞�1鏈�1鏃�00:00:00鑷充粖鐨勭鏁�,鍗冲綋鍓嶇殑鏃堕棿 + */ + //public static Long timeStamp = PayHelper.getTimeStamp(); + + /* + * 浜ゆ槗绫诲瀷锛屽皬绋嬪簭鍙栧�糐SAPI + */ + public static String tradeType = "JSAPI"; + + /* + * 绛惧悕绫诲瀷 + */ + //public static String signType = "MD5"; + public static String signType = "RSA"; + + /* + * 鍟嗗搧鎻忚堪 鍟嗗搧绠�鍗曟弿杩帮紝璇ュ瓧娈佃鎸夌収瑙勮寖浼犻�� + */ + //public static String body = "澶х鐮旂┒闄�-姘磋垂"; + public static String description = "澶х鐮旂┒闄�-姘磋垂"; + + /* + * 闄勫姞鏁版嵁锛屽湪鏌ヨAPI鍜屾敮浠橀�氱煡涓師鏍疯繑鍥烇紝鍙綔涓鸿嚜瀹氫箟鍙傛暟浣跨敤 + */ + public static String attach = "澶╂触"; + + /* + * 绛惧悕锛屽弬涓庣鍚嶅弬鏁帮細appid銆乤ttach銆乵ch_id銆乶once_str銆乥ody銆乷ut_trade_no銆乼otal_fee銆乻pbill_create_ip銆乶otify_url銆乼rade_type銆乷penid + */ + public String sign = ""; + + /** + * HTTP澶磋璇佺被鍨� + */ + public static String schema = "WECHATPAY2-SHA256-RSA2048"; + + /** + * 绉侀挜鏂囦欢璺緞 + */ + public static String certFileName = "C:\\webchat\\apiclient_key.pem"; + + /* + * 寰俊璁㈠崟鍙凤紝浼樺厛浣跨敤 + */ + public static String transactionid = ""; + + /* + * 鍟嗘埛绯荤粺鍐呴儴璁㈠崟鍙� + */ + public static String out_trade_no = ""; + + /* + * 鍟嗘埛閫�娆惧崟鍙� + */ + public static String out_refund_no = ""; + + /* + * 閫�娆鹃噾棰� + */ + public static Float refundfee; + + /* + * 璁㈠崟閲戦 + */ + public static Float totalfee; +} diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PaymentCtrl.java b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PaymentCtrl.java new file mode 100644 index 0000000..4b4b246 --- /dev/null +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PaymentCtrl.java @@ -0,0 +1,258 @@ +package com.dy.pipirrWebChat.payment; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.dy.common.aop.SsoAop; +import com.dy.common.webUtil.BaseResponse; +import com.dy.common.webUtil.BaseResponseUtils; +import com.dy.common.webUtil.ResultCodeMsg; +import com.dy.pipIrrGlobal.pojoSe.SeWebchatLogonState; +import com.dy.pipirrWebChat.util.OkHttpUtil; +import com.dy.pipirrWebChat.util.RestTemplateUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * @author ZhuBaoMin + * @date 2024-02-22 15:24 + * @LastEditTime 2024-02-22 15:24 + * @Description + */ + +@Slf4j +@Tag(name = "寰俊鏀粯绠$悊", description = "寰俊鏀粯鍚勭鎿嶄綔") +@RestController +@RequestMapping(path="payment") +@RequiredArgsConstructor +public class PaymentCtrl { + private final PaymentSv paymentSv; + private final RestTemplateUtil restTemplateUtil; + private String certFileName = PayInfo.certFileName; + + /** + * 鐧诲綍鍑瘉鏍¢獙 + * @param appid 灏忕▼搴� appId + * @param secret 灏忕▼搴� appSecret + * @param js_code 涓存椂鐧诲綍鍑瘉code + * @return + * @throws Exception + */ + @Operation(summary = "鐧诲綍鍑瘉鏍¢獙", description = "鐧诲綍鍑瘉鏍¢獙") + @ApiResponses(value = { + @ApiResponse( + responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE, + description = "鎿嶄綔缁撴灉锛歵rue锛氭垚鍔燂紝false锛氬け璐ワ紙BaseResponse.content锛�", + content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = Boolean.class))} + ) + }) + @PostMapping(path = "getSessionId") + @Transactional(rollbackFor = Exception.class) + @SsoAop() + public BaseResponse<Boolean> getSessionId(@RequestParam("appid") String appid, @RequestParam("secret") String secret, @RequestParam("js_code") String js_code) throws Exception { + String result = OkHttpUtil.builder().url("https://api.weixin.qq.com/sns/jscode2session") + .addParam("appid", appid) + .addParam("secret", secret) + .addParam("js_code", js_code) + .initGet() + .sync(); + JSONObject job = JSONObject.parseObject(result); + System.out.println(job.getString("session_key")); + + if(job.getLong("errcode") != null && job.getLong("errcode") >= -1) { + return BaseResponseUtils.buildFail("鐧诲綍鍑瘉鏍¢獙澶辫触"); + } + + // 娣诲姞鐧诲綍鎬佽褰� + SeWebchatLogonState po = new SeWebchatLogonState(); + po.setOpenId(job.getString("openid")); + po.setSessionKey(job.getString("session_key")); + Date createTime = new Date(); + po.setCreateTime(createTime); + Long id = paymentSv.insert(po); + if(id == null || id <= 0) { + return BaseResponseUtils.buildFail("鐧诲綍鎬佽褰曟坊鍔犲け璐�"); + } + + String SessionId = String.valueOf(id); + return BaseResponseUtils.buildSuccess(SessionId) ; + } + + @Operation(summary = "涓嬭浇骞冲彴璇佷功", description = "涓嬭浇骞冲彴璇佷功") + @ApiResponses(value = { + @ApiResponse( + responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE, + description = "鎿嶄綔缁撴灉锛歵rue锛氭垚鍔燂紝false锛氬け璐ワ紙BaseResponse.content锛�", + content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = Boolean.class))} + ) + }) + @GetMapping(path = "certificates") + @Transactional(rollbackFor = Exception.class) + @SsoAop() + public BaseResponse<JSONObject> certificates() throws Exception { + //String prepayId = ""; + //SeWebchatLogonState po = paymentSv.selectOne(Long.parseLong(sessionId)); + //String openid = po.getOpenId(); + + String method = "GET"; + String httpUrl = "/v3/certificates"; + String nonceStr = PayHelper.generateRandomString(); + Long timestamp = System.currentTimeMillis() / 1000; + + String header = PayInfo.schema + " " + PayHelper.getToken(method, httpUrl, "", nonceStr, timestamp, certFileName); + + Map<String, String> headers = new HashMap<>(); + headers.put("Authorization", header); + headers.put("Accept", "application/json"); + JSONObject job_result = restTemplateUtil.get(PayInfo.certificates,null, headers); + if(job_result != null) { + JSONArray array = job_result.getJSONArray("data"); + if(array != null && array.size() > 0) { + for(int i = 0; i < array.size(); i++) { + JSONObject job_data = array.getJSONObject(i); + String serial_no = job_data.getString("serial_no"); + String effective_time = job_data.getString("effective_time"); + String expire_time = job_data.getString("expire_time"); + JSONObject job_certificate = job_data.getJSONObject("encrypt_certificate"); + String algorithm = job_certificate.getString("algorithm"); + String nonce = job_certificate.getString("nonce"); + String associated_data = job_certificate.getString("associated_data"); + String ciphertext = job_certificate.getString("ciphertext"); + } + } + } + System.out.println(job_result.toJSONString()); + return BaseResponseUtils.buildSuccess(job_result.toJSONString()) ; + } + + /** + * JSAPI涓嬪崟 + * @param sessionId + * @param orderNumber + * @param payAmount + * @return + * @throws Exception + */ + @Operation(summary = "缁熶竴涓嬪崟", description = "缁熶竴涓嬪崟") + @ApiResponses(value = { + @ApiResponse( + responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE, + description = "鎿嶄綔缁撴灉锛歵rue锛氭垚鍔燂紝false锛氬け璐ワ紙BaseResponse.content锛�", + content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = Boolean.class))} + ) + }) + @PostMapping(path = "unifiedOrder") + @Transactional(rollbackFor = Exception.class) + @SsoAop() + public BaseResponse<Boolean> unifiedOrder(@RequestParam("sessionId") String sessionId, @RequestParam("orderNumber") String orderNumber, @RequestParam("payAmount") String payAmount) throws Exception { + String prepayId = ""; + SeWebchatLogonState po = paymentSv.selectOne(Long.parseLong(sessionId)); + String openid = po.getOpenId(); + + JSONObject job_body = new JSONObject(); + job_body.put("appid", PayInfo.appid); + job_body.put("mchid", PayInfo.mchid); + job_body.put("description", PayInfo.description); + job_body.put("out_trade_no", orderNumber); + job_body.put("notify_url", PayInfo.notifyUrl); + + //璁㈠崟閲戦 + JSONObject job_amount = new JSONObject(); + job_amount.put("total", 1); + job_amount.put("currency", "CNY"); + job_body.put("amount", job_amount); + + //鏀粯鑰� + JSONObject job_payer = new JSONObject(); + job_payer.put("openid", openid); + job_body.put("payer", job_payer); + + // 鑾峰彇闅忔満涓插拰鏃堕棿鎴筹紝鏀惧湪姝ゅ浠ヤ繚璇� + String nonceStr = PayHelper.generateRandomString(); + Long timestamp = System.currentTimeMillis() / 1000; + + String method = "POST"; + String httpUrl = "/v3/pay/transactions/jsapi"; + + String body = job_body.toJSONString(); + String header = PayInfo.schema + " " + PayHelper.getToken(method, httpUrl, body, nonceStr, timestamp, certFileName); + + Map<String, String> headers = new HashMap<>(); + headers.put("Authorization", header); + headers.put("Accept", "application/json"); + headers.put("Content-Type", "application/json"); + + JSONObject job_result = restTemplateUtil.post(PayInfo.orderUrl, body, headers); + if(job_result != null) { + System.out.println(job_result.toString()); + prepayId = job_result.getString("prepay_id"); + } + + + + //String result = OkHttpUtil.builder().url(PayInfo.orderUrl) + // .addBody(body) + // .addHeader("Authorization", header) + // .addHeader("Accept", "application/json") + // .addHeader("Content-Type", "application/json") + // .initPost(true) + // .sync(); + //System.out.println(result); + + return BaseResponseUtils.buildSuccess(prepayId) ; + } + + /** + * 鍐嶆绛惧悕 + * @param prepayId 棰勬敮浠樹氦鏄撲細璇濇爣璇� + * @return 灏忕▼搴忚皟璧锋敮浠樺弬鏁� + * @throws Exception + */ + @Operation(summary = "鍐嶆绛惧悕", description = "鍐嶆绛惧悕") + @ApiResponses(value = { + @ApiResponse( + responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE, + description = "鎿嶄綔缁撴灉锛歵rue锛氭垚鍔燂紝false锛氬け璐ワ紙BaseResponse.content锛�", + content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = Boolean.class))} + ) + }) + @GetMapping(path = "/signAgain") + @Transactional(rollbackFor = Exception.class) + @SsoAop() + public BaseResponse<JSONObject> signAgain(@RequestParam("prepayId") String prepayId) throws Exception { + + // 鑾峰彇闅忔満涓插拰鏃堕棿鎴筹紝鏀惧湪姝ゅ浠ヤ繚璇� + String appid = PayInfo.appid; + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + String nonceStr = PayHelper.generateRandomString(); + String pkg = "prepay_id=" + prepayId; + String signType = PayInfo.signType; + String message = PayHelper.buildMessage_signAgain(appid, timestamp, nonceStr, pkg); + String paySign = PayHelper.sign(message.getBytes("utf-8"), certFileName); + + JSONObject job_result = new JSONObject(); + job_result.put("timestamp", timestamp); + job_result.put("nonceStr", nonceStr); + job_result.put("package", pkg); + job_result.put("signType", signType); + job_result.put("paySign", paySign); + + return BaseResponseUtils.buildSuccess(job_result) ; + } +} diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PaymentSv.java b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PaymentSv.java new file mode 100644 index 0000000..11e8d4e --- /dev/null +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/PaymentSv.java @@ -0,0 +1,35 @@ +package com.dy.pipirrWebChat.payment; + +import com.dy.pipIrrGlobal.daoSe.SeWebchatLogonStateMapper; +import com.dy.pipIrrGlobal.pojoSe.SeWebchatLogonState; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * @author ZhuBaoMin + * @date 2024-02-22 17:39 + * @LastEditTime 2024-02-22 17:39 + * @Description + */ + +@Slf4j +@Service +public class PaymentSv { + @Autowired + private SeWebchatLogonStateMapper seWebchatLogonStateMapper; + + /** + * 娣诲姞鐧诲綍鎬佺姸鎬佽褰� + * @param po + * @return + */ + Long insert(SeWebchatLogonState po) { + seWebchatLogonStateMapper.insert(po); + return po.getId(); + } + + SeWebchatLogonState selectOne(Long id) { + return seWebchatLogonStateMapper.selectByPrimaryKey(id); + } +} diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/dto/Code2Session.java b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/dto/Code2Session.java new file mode 100644 index 0000000..21cc420 --- /dev/null +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/payment/dto/Code2Session.java @@ -0,0 +1,39 @@ +package com.dy.pipirrWebChat.payment.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * @author ZhuBaoMin + * @date 2024-02-22 15:34 + * @LastEditTime 2024-02-22 15:34 + * @Description + */ + +@Data +@Schema(name = "鐧诲綍鍑瘉鏍¢獙浼犲叆瀵硅薄") +public class Code2Session { + public static final long serialVersionUID = 202402221335001L; + + /** + * 灏忕▼搴� appId + */ + @Schema(description = "灏忕▼搴� appId", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @NotBlank(message = "灏忕▼搴� appId涓嶈兘涓虹┖") + private String appid; + + /** + * 灏忕▼搴� appSecret + */ + @Schema(description = "灏忕▼搴� appSecret", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @NotBlank(message = "灏忕▼搴� appSecret涓嶈兘涓虹┖") + private String secret; + + /** + * js_code + */ + @Schema(description = "js_code", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @NotBlank(message = "js_code涓嶈兘涓虹┖") + private String js_code; +} diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/ICallback.java b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/ICallback.java new file mode 100644 index 0000000..a40aa1d --- /dev/null +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/ICallback.java @@ -0,0 +1,15 @@ +package com.dy.pipirrWebChat.util; + +import okhttp3.Call; + +/** + * @author ZhuBaoMin + * @date 2024-02-22 15:22 + * @LastEditTime 2024-02-22 15:22 + * @Description + */ + +public interface ICallback { + void onSuccess(Call call, String data); + void onFail(Call call, String errorMsg); +} diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/OkHttpUtil.java b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/OkHttpUtil.java new file mode 100644 index 0000000..67c6ae0 --- /dev/null +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/OkHttpUtil.java @@ -0,0 +1,239 @@ +package com.dy.pipirrWebChat.util; + +import okhttp3.*; +import org.jetbrains.annotations.NotNull; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +/** + * @author ZhuBaoMin + * @date 2024-02-22 13:39 + * @LastEditTime 2024-02-22 13:39 + * @Description + */ + +public class OkHttpUtil { + + private static volatile OkHttpClient okHttpClient = null; + private static volatile Semaphore semaphore = null; + private Map<String, String> headerMap; + private Map<String, Object> paramMap; + private String url; + private Request.Builder request; + private String body; + + private OkHttpUtil() { + if (Objects.isNull(okHttpClient)) { + synchronized (OkHttpUtil.class) { + if (Objects.isNull(okHttpClient)) { + TrustManager[] trustManagers = buildTrustManager(); + okHttpClient = new OkHttpClient.Builder() + .connectTimeout(15, TimeUnit.SECONDS) + .writeTimeout(20, TimeUnit.SECONDS) + .readTimeout(20, TimeUnit.SECONDS) + .sslSocketFactory(createSSLSocketFactory(trustManagers), (X509TrustManager)trustManagers[0]) + .hostnameVerifier((hostname, session) -> true) + .retryOnConnectionFailure(true) + .build(); + addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"); + } + } + } + } + + public OkHttpUtil initGet() { + request = new Request.Builder().get(); + StringBuilder builder = new StringBuilder(url); + if (Objects.nonNull(paramMap)) { + builder.append("?"); + paramMap.forEach((key, value) -> { + try { + builder.append(URLEncoder.encode(key, "utf-8")) + .append("=") + .append(URLEncoder.encode((String)value, "utf-8")) + .append("&"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + }); + builder.deleteCharAt(builder.length() - 1); + } + request.url(builder.toString()); + return this; + } + + public OkHttpUtil initPost(boolean isJson) { + RequestBody requestBody = null; + if (isJson) { + requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), body); + } else { + FormBody.Builder formBody = new FormBody.Builder(); + if (Objects.nonNull(paramMap)) { + paramMap.forEach((x, y) -> formBody.add(x, (String) y)); + } + requestBody = formBody.build(); + } + request = new Request.Builder().post(requestBody).url(url); + return this; + } + + /** + * @Description:鍚屾璇锋眰 + * @Author: zzc + * @Date: 2022-12-04 18:06 + * @return: java.lang.String + **/ + public String sync() throws Exception { + setHeader(request); + try { + Response result = okHttpClient.newCall(request.build()).execute(); + if (result.isSuccessful()) { + return result.body().string(); + } + throw new Exception(result.body().string()); + } catch (IOException e) { + throw new Exception(e.getMessage()); + } + } + + /** + * @Description:寮傛璇锋眰锛屾湁杩斿洖鍊� + * @Author: zzc + * @Date: 2022-12-04 18:05 + * @return: java.lang.String + **/ + public String async() { + StringBuffer buffer = new StringBuffer(); + setHeader(request); + okHttpClient.newCall(request.build()).enqueue(new Callback() { + @Override + public void onResponse(@NotNull okhttp3.Call call, @NotNull Response response) throws IOException { + if (Objects.nonNull(response.body())) { + buffer.append(response.body().string()); + getSemaphore().release(); + } + } + @Override + public void onFailure(@NotNull okhttp3.Call call, @NotNull IOException e) { + buffer.append("璇锋眰鍑洪敊").append(e.getMessage()); + } + }); + try { + getSemaphore().acquire(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return buffer.toString(); + } + + private static Semaphore getSemaphore() { + synchronized (OkHttpUtil.class) { + if (Objects.isNull(semaphore)) { + semaphore = new Semaphore(0); + } + } + return semaphore; + } + + public static OkHttpUtil builder() { + return new OkHttpUtil(); + } + + public OkHttpUtil url(String url) { + this.url = url; + return this; + } + + public OkHttpUtil addParam(String key, String value) { + if (Objects.isNull(paramMap)) { + paramMap = new LinkedHashMap<>(16); + } + paramMap.put(key, value); + return this; + } + + public OkHttpUtil addBody(String body) { + this.body = body; + return this; + } + + public void setHeader(Request.Builder request) { + if (Objects.nonNull(headerMap)) { + headerMap.forEach(request::addHeader); + } + } + + public OkHttpUtil addHeader(String key, String value) { + if (Objects.isNull(headerMap)) { + headerMap = new LinkedHashMap<>(16); + } + headerMap.put(key, value); + return this; + } + + /** + * @Description:鐢熸垚瀹夊叏濂楁帴瀛楀伐鍘傦紝鐢ㄤ簬Https璇锋眰鐨勮瘉涔﹁烦杩� + * @Author: zzc + * @Date: 2022-11-30 16:03 + * @param trustManagers: + * @return: javax.net.ssl.SSLSocketFactory + **/ + //private static SSLSocketFactory createSSLSocketFactory(TrustManager[] trustManagers) { + // SSLSocketFactory sslSocketFactory = null; + // try { + // SSLContext ssl = SSLContext.getInstance("SSL"); + // ssl.init(null, trustManagers, new SecureRandom()); + // sslSocketFactory = ssl.getSocketFactory(); + // } catch (Exception e) { + // e.printStackTrace(); + // } + // return sslSocketFactory; + //} + + private static SSLSocketFactory createSSLSocketFactory(TrustManager[] trustManagers) { + SSLSocketFactory sslSocketFactory = null; + try { + SSLContext ssl = SSLContext.getInstance("SSL"); + ssl.init(null, trustManagers, new SecureRandom()); + sslSocketFactory = ssl.getSocketFactory(); + + } catch (Exception e) { + e.printStackTrace(); + } + return sslSocketFactory; + } + + private static TrustManager[] buildTrustManager() { + return new TrustManager[] { + new X509TrustManager() { + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + } + + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[]{}; + } + } + }; + } + +} diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/RestTemplateUtil.java b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/RestTemplateUtil.java new file mode 100644 index 0000000..980a121 --- /dev/null +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/java/com/dy/pipirrWebChat/util/RestTemplateUtil.java @@ -0,0 +1,102 @@ +package com.dy.pipirrWebChat.util; + +import com.alibaba.fastjson2.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.*; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * @author ZhuBaoMin + * @date 2024-02-23 15:42 + * @LastEditTime 2024-02-23 15:42 + * @Description + */ + +@Component +public class RestTemplateUtil { + + @Autowired + private RestTemplate restTemplate; + + //@Qualifier("RestTemplateWithCert") + //@Resource + //private RestTemplate restTemplate; + + public JSONObject get(String url, Map<String, Object> queryParams) throws IOException { + return get(url, queryParams, new HashMap<>(1)); + } + + public JSONObject get(String url, Map<String, Object> queryParams, Map<String, String> headerParams) throws IOException { + String tempUrl = setParamsByAppendUrl(queryParams, url); + HttpHeaders headers = new HttpHeaders(); + headerParams.forEach(headers::add); + HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(null, headers); + ResponseEntity<String> response = restTemplate.exchange(tempUrl, HttpMethod.GET, httpEntity, String.class); + return JSONObject.parseObject(response.getBody()); + } + + public JSONObject get2(String url, Map<String, Object> queryParams, Map<String, String> headerParams) throws IOException { + String tempUrl = setParamsByPath(queryParams, url); + HttpHeaders headers = new HttpHeaders(); + headerParams.forEach(headers::add); + HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(null, headers); + ResponseEntity<String> response = restTemplate.exchange(tempUrl, HttpMethod.GET, httpEntity, String.class, queryParams); + return JSONObject.parseObject(response.getBody()); + } + + public JSONObject post(String url, String json, Map<String, String> headerParams) { + HttpHeaders headers = new HttpHeaders(); + headerParams.forEach(headers::add); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.add("Accept", MediaType.APPLICATION_JSON.toString()); + HttpEntity<String> httpEntity = new HttpEntity<>(json, headers); + ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, httpEntity, String.class); + return JSONObject.parseObject(response.getBody()); + } + + private String setParamsByPath(Map<String, Object> queryParams, String url) { + // url?id={id}&name={name} + if (queryParams == null || queryParams.isEmpty()) { + return url; + } + StringBuilder sb = new StringBuilder(); + try { + for (Map.Entry<String, Object> entry : queryParams.entrySet()) { + sb.append("&").append(entry.getKey()).append("=").append("{").append(entry.getKey()).append("}"); + } + if (!url.contains("?")) { + sb.deleteCharAt(0).insert(0, "?"); + } + } catch (Exception e) { + e.printStackTrace(); + } + return url + sb; + } + + private String setParamsByAppendUrl(Map<String, Object> queryParams, String url) { + // url?id=1&name=zzc + if (queryParams == null || queryParams.isEmpty()) { + return url; + } + StringBuilder sb = new StringBuilder(); + try { + for (Map.Entry<String, Object> entry : queryParams.entrySet()) { + sb.append("&").append(entry.getKey()).append("="); + sb.append(entry.getValue()); + } + if (!url.contains("?")) { + sb.deleteCharAt(0).insert(0, "?"); + } + } catch (Exception e) { + e.printStackTrace(); + } + return url + sb; + } + +} diff --git a/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/resources/application.yml b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/resources/application.yml index 5afebda..3702633 100644 --- a/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/resources/application.yml +++ b/pipIrr-platform/pipIrr-web/pipIrr-web-webchat/src/main/resources/application.yml @@ -5,12 +5,12 @@ #actutor鐨剋eb绔彛 management: server: - port: ${pipIrr.project.actutorPort} + port: ${pipIrr.webchat.actutorPort} #web鏈嶅姟绔彛锛�8086 server: port: ${pipIrr.webchat.webPort} servlet: - context-path: /webchat #web璁块棶涓婁笅鏂囪矾寰� + context-path: /webchat #webchat璁块棶涓婁笅鏂囪矾寰� context-parameters: #GenerateIdSetSuffixListener涓簲鐢紝鍙栧�艰寖鍥存槸0-99 idSuffix: ${pipIrr.webchat.idSuffix} -- Gitblit v1.8.0