mms-doc 接口支持

This commit is contained in:
MMS 2025-05-29 00:32:09 +08:00
parent 6609ad2ff5
commit c8a23f841a
33 changed files with 802 additions and 170 deletions

View File

@ -16,7 +16,7 @@ ENV SERVER_PORT=8080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
EXPOSE ${SERVER_PORT} EXPOSE ${SERVER_PORT}
ADD ./target/mms-system.jar ./app.jar ADD ./target/mms-admin.jar ./app.jar
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \ ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
# 应用名称 如果想区分集群节点监控 改成不同的名称即可 # 应用名称 如果想区分集群节点监控 改成不同的名称即可

View File

@ -51,7 +51,7 @@ spring:
driverClassName: com.mysql.cj.jdbc.Driver driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mms?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true url: jdbc:mysql://localhost:3306/mms?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
username: mms username: mms
password: 123456 password: ZKKxz8KGmpGfLGf3
# 从库数据源 # 从库数据源
slave: slave:
lazy: true lazy: true
@ -59,7 +59,7 @@ spring:
driverClassName: com.mysql.cj.jdbc.Driver driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mms?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true url: jdbc:mysql://localhost:3306/mms?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
username: mms username: mms
password: 123456 password: ZKKxz8KGmpGfLGf3
# oracle: # oracle:
# type: ${spring.datasource.type} # type: ${spring.datasource.type}
# driverClassName: oracle.jdbc.OracleDriver # driverClassName: oracle.jdbc.OracleDriver

View File

@ -4,15 +4,13 @@ FROM openjdk:17.0.2-oraclelinux8
MAINTAINER SXPCWLKJ MAINTAINER SXPCWLKJ
RUN mkdir -p /sxpcwlkj \ RUN mkdir -p /sxpcwlkj \
/sxpcwlkj/mms-mobile \ /sxpcwlkj/mms-doc \
/sxpcwlkj/mms-mobile/logs \ /sxpcwlkj/mms-doc/logs \
/sxpcwlkj/mms-mobile/files \ /sxpcwlkj/mms-doc/files
/sxpcwlkj/mms-mobile/temp \
/sxpcwlkj/mms-mobile/skywalking/agent
WORKDIR /sxpcwlkj/mms-mobile WORKDIR /sxpcwlkj/mms-doc
ENV SERVER_PORT=8089 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" ENV SERVER_PORT=8070 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
EXPOSE ${SERVER_PORT} EXPOSE ${SERVER_PORT}

View File

@ -1,19 +1,49 @@
package com.sxpcwlkj.docApi.controller; package com.sxpcwlkj.docApi.controller;
import cn.dev33.satoken.annotation.SaIgnore; import cn.dev33.satoken.annotation.SaIgnore;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
import com.github.binarywang.wxpay.service.WxPayService;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.enums.TradeType;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApiConfigKit;
import com.sxpcwlkj.authority.LoginObject;
import com.sxpcwlkj.common.code.entity.WxCodeBo;
import com.sxpcwlkj.common.enums.WxCodeStatusEnum;
import com.sxpcwlkj.common.utils.*;
import com.sxpcwlkj.docApi.entity.DocOrder;
import com.sxpcwlkj.docApi.entity.DocProduct;
import com.sxpcwlkj.docApi.entity.DocUser; import com.sxpcwlkj.docApi.entity.DocUser;
import com.sxpcwlkj.docApi.entity.bo.DocOrderBo;
import com.sxpcwlkj.docApi.entity.bo.MyRequest;
import com.sxpcwlkj.docApi.entity.vo.DocOrderVo;
import com.sxpcwlkj.docApi.entity.vo.DocUserVo; import com.sxpcwlkj.docApi.entity.vo.DocUserVo;
import com.sxpcwlkj.docApi.mapper.DocConfigMapper;
import com.sxpcwlkj.docApi.mapper.DocOrderMapper;
import com.sxpcwlkj.docApi.mapper.DocProductMapper;
import com.sxpcwlkj.docApi.service.DocOrderService;
import com.sxpcwlkj.docApi.service.DocUserService; import com.sxpcwlkj.docApi.service.DocUserService;
import com.sxpcwlkj.docApi.utils.DocBaseTool; import com.sxpcwlkj.docApi.utils.DocBaseTool;
import com.sxpcwlkj.docApi.utils.DocR; import com.sxpcwlkj.docApi.utils.DocR;
import com.sxpcwlkj.framework.utils.SignUtil;
import com.sxpcwlkj.redis.RedisUtil;
import com.sxpcwlkj.wx.service.WxCodeService;
import com.sxpcwlkj.wx.service.WxOrderService;
import com.sxpcwlkj.wx.service.WxService;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
/**
* @author shanpengnian
*/
@Slf4j @Slf4j
@Validated @Validated
@RequiredArgsConstructor @RequiredArgsConstructor
@ -22,13 +52,235 @@ import org.springframework.web.bind.annotation.RestController;
public class DocUserController extends DocBaseTool { public class DocUserController extends DocBaseTool {
private final DocUserService docUserService; private final DocUserService docUserService;
private final DocOrderMapper docOrderMapper;
private final DocConfigMapper docConfigMapper;
private final WxCodeService wxCodeService;
private final DocProductMapper docProductMapper;
private final WxOrderService wxOrderService;
private final WxService wxService;
private final DocOrderService docOrderService;
/**
* 获取用户信息
* @param request 请求
* @param response 响应
* @return 用户信息
*/
@SaIgnore @SaIgnore
@PostMapping("/userinfo") @PostMapping("/userinfo")
public DocR<DocUserVo> userinfo(HttpServletRequest request){ public DocR<DocUserVo> userinfo(HttpServletRequest request, HttpServletResponse response){
DocUserVo docUserVo = docUserService.selectVoById("1"); DocUserVo docUserVo = docUserService.selectVoById(getUserId(request));
docUserVo.setVip_date("2025-12-12"); if(docUserVo==null){
return DocR.error("99910","会话过期");
}
docOrderMapper.delete(new LambdaQueryWrapper<DocOrder>().eq(DocOrder::getUid,docUserVo.getUid())
.eq(DocOrder::getStatus,0)
.le(DocOrder::getCtime,DateUtil.getAddDate(new Date(),0,0,0,1,0,0,0))
);
List<DocOrderVo> orderVos= docOrderMapper.selectVoList(new LambdaQueryWrapper<DocOrder>()
.eq(DocOrder::getUid,docUserVo.getUid())
.eq(DocOrder::getStatus, 1)
.orderByAsc(DocOrder::getCtime));
//根据付款时间计算累计的VIP天数
Date expireTime=docUserVo.getCtime();
for (DocOrderVo docOrderVo : orderVos) {
//判断订单时间是否大于 到期时间
if(docOrderVo.getCtime().after(expireTime)){
expireTime=docOrderVo.getCtime();
}
String day= docConfigMapper.selectByKey(docOrderVo.getProdId());
if(day==null){
day="0";
}
expireTime=DateUtil.getAddDate(expireTime,0,0, Integer.parseInt(day),0,0,0,0);
}
if(!new Date().after(expireTime)){
docUserVo.setType("vip");
docUserVo.setVip_date(expireTime);
}else {
docUserVo.setVip_date(DateUtil.getStrToDate("2025-01-01 00:00:00"));
docUserVo.setType("usr");
}
CookieUtil.setCookie(response,"mss",getToken(docUserVo.getUid()),1000*60*60*24*7);
return DocR.ok(docUserVo); return DocR.ok(docUserVo);
} }
/**
* 登录二维码
* @return 二维码
*/
@SaIgnore
@PostMapping("/oauth-authorize")
public DocR<Map<String,String>> oauthAuthorize(){
Map<String,String> data= new HashMap<>();
String state=RandomUtil.getRandomUUID();
String codeUrl= wxCodeService.getCode(new WxCodeBo(state)
.typeDocLogin()
.expireTime(1000*60)
.paramData(state));
data.put("url",codeUrl);
data.put("state",state);
return DocR.ok(data);
}
/**
* 登录二维码轮询
* @param request 请求
* @return 登录状态
*/
@SaIgnore
@PostMapping("/oauth-polling")
public DocR<Map<String,String>> oauthPolling(@RequestBody MyRequest bo){
String state = bo.getState();
Map<String,String> data= new HashMap<>();
data.put("status","0");
if(state==null){
return DocR.error("50001","state不能为空");
}
//登录二维码
WxCodeBo wxCodeBo= wxCodeService.getCodeState(new WxCodeBo(state).typeDocLogin());
if(Objects.equals(wxCodeBo.getState(), WxCodeStatusEnum.SUCCEED.getValue())){
log.info(wxCodeBo.getOpenId());
DocUser docUser= docUserService.bindingOpenId(wxCodeBo.getOpenId());
data.put("status","1");
data.put("token",getToken(docUser.getUid()));
}
return DocR.ok(data);
}
/**
* 商品列表
* @param request 请求
* @return 商品列表
*/
@SaIgnore
@PostMapping("/product-list")
public DocR<Map<String,Object>> productList(HttpServletRequest request){
String uid= getUserId(request);
List<DocProduct> docProducts= docProductMapper.selectList(new LambdaQueryWrapper<DocProduct>().eq(DocProduct::getStatus,1).orderByAsc(DocProduct::getSort));
Map<String,Object> endData= new HashMap<>();
List<Map<String,String>> data= new ArrayList<>();
for (DocProduct docProduct : docProducts) {
Map<String,String> map= new HashMap<>();
map.put("prod_id",docProduct.getProdId());
map.put("prod_name",docProduct.getProdName());
map.put("unit_price",docProduct.getUnitPrice());
map.put("mark_price",docProduct.getMarkPrice());
map.put("intro",docProduct.getType());
DocUserVo docUserVo= docUserService.selectVoById(uid);
if(docUserVo==null){
return DocR.error("99910","会话过期");
}
Map<String,Object> orderInfo= new HashMap<>();
orderInfo.put("openId",docUserVo.getOpenId());
orderInfo.put("orderNo",System.currentTimeMillis()+"");
orderInfo.put("productTitle",docProduct.getProdName());
orderInfo.put("productId",docProduct.getProdId());
orderInfo.put("productType",docProduct.getType());
orderInfo.put("payPrice",docProduct.getUnitPrice());
orderInfo.put("ip",IPUtil.getIp(request));
orderInfo.put("tradeType", TradeType.NATIVE.getTradeType());
orderInfo.put("mchId","");
orderInfo.put("expireTime",System.currentTimeMillis()+1000*60*3);
orderInfo.put("notifyUrl","https://mmsadmin.cn/vpapi/meb/payNotify");
R<Object> r= wxOrderService.createPay(orderInfo);
map.put("buy_url",r.getData().toString());
data.add(map);
docOrderService.create(docUserVo,orderInfo);
}
endData.put("items",data);
return DocR.ok(endData);
}
/**
* 付款状态轮询
* @param request 请求
* @return 登录状态
*/
@SaIgnore
@PostMapping("/product-buy-qry")
public DocR<Map<String,String>> productBuyQry(@RequestBody MyRequest bo,HttpServletRequest request){
String prodId = bo.getProd_id();
Map<String,String> data= new HashMap<>();
data.put("status","0");
if(prodId==null){
return DocR.error("50001","prodId不能为空");
}
data.put("status",docOrderService.selectPayState(prodId,getUserId(request)));
return DocR.ok(data);
}
@SaIgnore
@PostMapping("/notify")
public String notify(HttpServletRequest req, HttpServletResponse resp, @RequestBody String body){
WxPayOrderNotifyResult result = null;
try {
System.out.println("X=:" + body);
result = wxService.getWxPayService(TradeType.NATIVE.getTradeType()).parseOrderNotifyResult(body,TradeType.NATIVE.getTradeType());
// 结果正确 outTradeNo
log.warn("交易单号:" + result.getTransactionId());
log.warn("订单号:" + result.getOutTradeNo());
log.warn("付款金额:" + BaseWxPayResult.fenToYuan(result.getTotalFee()));
if ("SUCCESS".equals(result.getResultCode())) {
log.warn("支付回调进来了:付款成功");
// 更新订单状态
// 更新库存
// 发送通知
// 发送邮件
// 发送短信
// 发送微信消息
docOrderService.updateByOrderNo(result.getTransactionId());
return "SUCCESS";
} else {
log.warn("支付回调进来了:付款失败");
}
} catch (Exception e) {
log.error("商品支付报错了",e);
}
return null;
}
/**
* 异步通知
*/
@RequestMapping(value = "/payNotify", method = {RequestMethod.POST, RequestMethod.GET})
@ResponseBody
@SaIgnore
public String payNotify(HttpServletRequest request,@RequestBody String body) {
String xmlMsg = body;
log.info("支付通知=" + xmlMsg);
Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);
String returnCode = params.get("return_code");
// 注意重复通知的情况同一订单号可能收到多次通知请注意一定先判断订单状态
// 注意此处签名方式需与统一下单的签名类型一致
if (wxOrderService.verifyNotify(params)) {
if (WxPayKit.codeIsOk(returnCode)) {
// 更新订单信息
docOrderService.updateByOrderNo(params.get("out_trade_no"));
// 发送通知等
Map<String, String> xml = new HashMap<String, String>(2);
xml.put("return_code", "SUCCESS");
xml.put("return_msg", "OK");
return WxPayKit.toXml(xml);
}
}
return null;
}
} }

View File

@ -7,6 +7,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date;
/** /**
* 文档订单 * 文档订单
@ -62,9 +63,9 @@ public class DocOrder extends BaseEntity {
/** /**
* 创建时间 * 创建时间
*/ */
private String ctime; private Date ctime;
/** /**
* 更新时间 * 更新时间
*/ */
private String mtime; private Date mtime;
} }

View File

@ -1,5 +1,6 @@
package com.sxpcwlkj.docApi.entity; package com.sxpcwlkj.docApi.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import com.sxpcwlkj.datasource.entity.BaseEntity; import com.sxpcwlkj.datasource.entity.BaseEntity;
@ -43,4 +44,9 @@ public class DocUser extends BaseEntity {
* 更新时间 * 更新时间
*/ */
private Date mtime; private Date mtime;
/**
* 微信ID
*/
private String openId;
} }

View File

@ -11,6 +11,7 @@ import lombok.EqualsAndHashCode;
import java.io.Serial; import java.io.Serial;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date;
/** /**
* 文档订单Bo * 文档订单Bo
@ -79,10 +80,10 @@ public class DocOrderBo extends BaseEntity {
* 创建时间 * 创建时间
*/ */
@NotBlank(message = "创建时间不能为空" ,groups = {ValidatedGroupConfig.insert.class,ValidatedGroupConfig.update.class}) @NotBlank(message = "创建时间不能为空" ,groups = {ValidatedGroupConfig.insert.class,ValidatedGroupConfig.update.class})
private String ctime; private Date ctime;
/** /**
* 更新时间 * 更新时间
*/ */
@NotBlank(message = "更新时间不能为空" ,groups = {ValidatedGroupConfig.insert.class,ValidatedGroupConfig.update.class}) @NotBlank(message = "更新时间不能为空" ,groups = {ValidatedGroupConfig.insert.class,ValidatedGroupConfig.update.class})
private String mtime; private Date mtime;
} }

View File

@ -0,0 +1,14 @@
package com.sxpcwlkj.docApi.entity.bo;
import lombok.Data;
/**
* @author xijue
*/
@Data
public class MyRequest {
private String state;
private String prod_id;
}

View File

@ -9,6 +9,7 @@ import lombok.EqualsAndHashCode;
import java.io.Serial; import java.io.Serial;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date;
/** /**
* 文档订单Vo * 文档订单Vo
@ -67,10 +68,10 @@ public class DocOrderVo extends BaseEntityVo{
/** /**
* 创建时间 * 创建时间
*/ */
private String ctime; private Date ctime;
/** /**
* 更新时间 * 更新时间
*/ */
private String mtime; private Date mtime;
} }

View File

@ -53,9 +53,8 @@ public class DocUserVo extends BaseEntityVo{
@JsonFormat(pattern = DateUtil.DATE_TIME_PATTERN) @JsonFormat(pattern = DateUtil.DATE_TIME_PATTERN)
private Date mtime; private Date mtime;
/** private Date vip_date;
* 用户等级到期时间
*/ private String openId;
private String vip_date;
} }

View File

@ -11,7 +11,7 @@ public enum DefStaticEnum implements IEnum {
/** /**
* 会员默认注册头像 * 会员默认注册头像
*/ */
MEMBER_DEF_HEADER_IMG("MEMBER_DEF_HEADER_IMG","https://jifugou.oss-cn-zhangjiakou.aliyuncs.com/01_default/defHeadImg.png"), MEMBER_DEF_HEADER_IMG("MEMBER_DEF_HEADER_IMG","https://picsum.photos/30/30"),
; ;

View File

@ -4,6 +4,8 @@ import com.sxpcwlkj.datasource.mapper.BaseMapperPlus;
import com.sxpcwlkj.docApi.entity.DocConfig; import com.sxpcwlkj.docApi.entity.DocConfig;
import com.sxpcwlkj.docApi.entity.vo.DocConfigVo; import com.sxpcwlkj.docApi.entity.vo.DocConfigVo;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
/** /**
@ -16,4 +18,6 @@ import org.springframework.stereotype.Repository;
@Repository @Repository
public interface DocConfigMapper extends BaseMapperPlus<DocConfig, DocConfigVo> { public interface DocConfigMapper extends BaseMapperPlus<DocConfig, DocConfigVo> {
@Select("select COALESCE(c.value, 0) AS value from doc_config c left join doc_product p on c.`key`=p.code where p.prod_id=#{prodId}")
String selectByKey(@Param("prodId") String prodId);
} }

View File

@ -1,19 +0,0 @@
package com.sxpcwlkj.docApi.service;
import com.sxpcwlkj.docApi.entity.DocAuthorizeUser;
import com.sxpcwlkj.docApi.entity.bo.DocAuthorizeUserBo;
import com.sxpcwlkj.docApi.entity.vo.DocAuthorizeUserVo;
import com.sxpcwlkj.framework.sercice.BaseService;
import java.util.Set;
/**
* 文档授权用户-接口
*
* @author 西决
* @Doc <a href='https://www.mmsadmin.com'>MMS文档</a>
* @describe 支持自定义扩展,已继承接口{insertdeleteByIdupdateByIdselectByIdgetByEntityListPage}更多查看BaseService接口
*/
public interface DocAuthorizeUserService extends BaseService<DocAuthorizeUser, DocAuthorizeUserVo, DocAuthorizeUserBo> {
}

View File

@ -1,19 +0,0 @@
package com.sxpcwlkj.docApi.service;
import com.sxpcwlkj.docApi.entity.DocConfig;
import com.sxpcwlkj.docApi.entity.bo.DocConfigBo;
import com.sxpcwlkj.docApi.entity.vo.DocConfigVo;
import com.sxpcwlkj.framework.sercice.BaseService;
import java.util.Set;
/**
* 文档配置-接口
*
* @author 西决
* @Doc <a href='https://www.mmsadmin.com'>MMS文档</a>
* @describe 支持自定义扩展,已继承接口{insertdeleteByIdupdateByIdselectByIdgetByEntityListPage}更多查看BaseService接口
*/
public interface DocConfigService extends BaseService<DocConfig, DocConfigVo, DocConfigBo> {
}

View File

@ -3,8 +3,10 @@ package com.sxpcwlkj.docApi.service;
import com.sxpcwlkj.docApi.entity.DocOrder; import com.sxpcwlkj.docApi.entity.DocOrder;
import com.sxpcwlkj.docApi.entity.bo.DocOrderBo; import com.sxpcwlkj.docApi.entity.bo.DocOrderBo;
import com.sxpcwlkj.docApi.entity.vo.DocOrderVo; import com.sxpcwlkj.docApi.entity.vo.DocOrderVo;
import com.sxpcwlkj.docApi.entity.vo.DocUserVo;
import com.sxpcwlkj.framework.sercice.BaseService; import com.sxpcwlkj.framework.sercice.BaseService;
import java.util.Map;
import java.util.Set; import java.util.Set;
/** /**
@ -16,4 +18,9 @@ import java.util.Set;
*/ */
public interface DocOrderService extends BaseService<DocOrder, DocOrderVo, DocOrderBo> { public interface DocOrderService extends BaseService<DocOrder, DocOrderVo, DocOrderBo> {
Boolean create(DocUserVo docUserVo, Map<String, Object> orderInfo);
String selectPayState(String prodId,String uid);
Boolean updateByOrderNo(String transactionId);
} }

View File

@ -1,18 +0,0 @@
package com.sxpcwlkj.docApi.service;
import com.sxpcwlkj.docApi.entity.DocProduct;
import com.sxpcwlkj.docApi.entity.bo.DocProductBo;
import com.sxpcwlkj.docApi.entity.vo.DocProductVo;
import com.sxpcwlkj.framework.sercice.BaseService;
import java.util.Set;
/**
* 文档商品-接口
*
* @author 西决
* @Doc <a href='https://www.mmsadmin.com'>MMS文档</a>
* @describe 支持自定义扩展,已继承接口{insertdeleteByIdupdateByIdselectByIdgetByEntityListPage}更多查看BaseService接口
*/
public interface DocProductService extends BaseService<DocProduct, DocProductVo, DocProductBo> {
}

View File

@ -16,4 +16,10 @@ import java.util.Set;
*/ */
public interface DocUserService extends BaseService<DocUser, DocUserVo, DocUserBo> { public interface DocUserService extends BaseService<DocUser, DocUserVo, DocUserBo> {
/**
* 绑定openId
* @param openId openId
* @return 用户
*/
DocUser bindingOpenId(String openId);
} }

View File

@ -0,0 +1,146 @@
package com.sxpcwlkj.docApi.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sxpcwlkj.common.utils.DataUtil;
import com.sxpcwlkj.common.utils.MapstructUtil;
import com.sxpcwlkj.datasource.entity.page.PageQuery;
import com.sxpcwlkj.datasource.entity.page.TableDataInfo;
import com.sxpcwlkj.datasource.mapper.BaseMapperPlus;
import com.sxpcwlkj.docApi.entity.DocOrder;
import com.sxpcwlkj.docApi.entity.bo.DocOrderBo;
import com.sxpcwlkj.docApi.entity.vo.DocOrderVo;
import com.sxpcwlkj.docApi.entity.vo.DocUserVo;
import com.sxpcwlkj.docApi.mapper.DocOrderMapper;
import com.sxpcwlkj.docApi.service.DocOrderService;
import com.sxpcwlkj.framework.sercice.impl.BaseServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author shanpengnian
*/
@Slf4j
@Transactional
@Service("doc_order")
@RequiredArgsConstructor
public class DocOrderServiceImpl extends BaseServiceImpl<DocOrder, DocOrderVo, DocOrderBo> implements DocOrderService {
private final DocOrderMapper baseMapper;
@Override
public BaseMapperPlus<DocOrder, DocOrderVo> getBaseMapper() {
return baseMapper;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insert(DocOrderBo bo) {
try {
int row;
bo.setOrderId(null);
DocOrder obj = MapstructUtil.convert(bo, DocOrder.class);
assert obj != null;
row = this.getBaseMapper().insert(obj);
bo.setOrderId(obj.getOrderId());
return row > 0;
} catch (Exception e) {
log.error("文档订单,insert 操作失败", e);
throw e;
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteById(Serializable ids) {
try {
String[] array = DataUtil.getCatStr(ids.toString(), ",");
return this.getBaseMapper().deleteByIds(new ArrayList<>(List.of(array)))>0;
} catch (Exception e) {
log.error("文档订单,deleteById 操作失败", e);
throw e;
}
}
@Override
public Boolean updateById(DocOrderBo bo) {
try {
int row;
DocOrder obj = MapstructUtil.convert(bo, DocOrder.class);
row = this.getBaseMapper().updateById(obj);
return row > 0;
} catch (Exception e) {
log.error("文档订单,updateById 操作失败", e);
throw e;
}
}
@Override
public DocOrderVo selectVoById(Serializable id) {
return this.getBaseMapper().selectVoById(id);
}
@Override
public TableDataInfo<DocOrderVo> selectListVoPage(DocOrderBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<DocOrder> lqw = buildQueryWrapper(bo);
Page<DocOrderVo> page = baseMapper.selectVoPage(pageQuery.build(),lqw);
return TableDataInfo.build(page);
}
private LambdaQueryWrapper<DocOrder> buildQueryWrapper(DocOrderBo query){
if(query==null){
query=new DocOrderBo();
}
LambdaQueryWrapper<DocOrder> wrapper = Wrappers.lambdaQuery();
return wrapper;
}
@Override
public Boolean create(DocUserVo docUserVo, Map<String, Object> orderInfo) {
DocOrderBo docOrderBo= new DocOrderBo();
docOrderBo.setUid(docUserVo.getUid());
docOrderBo.setTxnAmt(new BigDecimal(orderInfo.get("payPrice").toString()));
docOrderBo.setProdId(orderInfo.get("productId").toString());
docOrderBo.setProdName(orderInfo.get("productTitle").toString());
docOrderBo.setProdPrice(new BigDecimal(orderInfo.get("payPrice").toString()));
docOrderBo.setProdType(orderInfo.get("productType").toString());
docOrderBo.setStatus(0);
docOrderBo.setPayNo(orderInfo.get("orderNo").toString());
docOrderBo.setPayTimeout(orderInfo.get("expireTime").toString());
docOrderBo.setCtime(new Date());
docOrderBo.setMtime(new Date());
return this.insert(docOrderBo);
}
@Override
public String selectPayState(String prodId,String uid) {
DocOrder docOrder= baseMapper.selectOne(new LambdaQueryWrapper<DocOrder>().eq(DocOrder::getProdId,prodId).eq(DocOrder::getUid,uid).eq(DocOrder::getStatus,1).orderByDesc(DocOrder::getCtime));
if(docOrder!=null){
return "finish";
}
return "unpaid";
}
@Override
public Boolean updateByOrderNo(String transactionId) {
DocOrder docOrder= baseMapper.selectOne(new LambdaQueryWrapper<DocOrder>().eq(DocOrder::getPayNo,transactionId));
if(docOrder!=null){
docOrder.setStatus(1);
docOrder.setMtime(new Date());
return baseMapper.updateById(docOrder)>0;
}
return false;
}
}

View File

@ -12,8 +12,10 @@ import com.sxpcwlkj.docApi.entity.DocUser;
import com.sxpcwlkj.docApi.entity.bo.DocUserBo; import com.sxpcwlkj.docApi.entity.bo.DocUserBo;
import com.sxpcwlkj.docApi.entity.vo.DocUserVo; import com.sxpcwlkj.docApi.entity.vo.DocUserVo;
import com.sxpcwlkj.docApi.enums.DefStaticEnum;
import com.sxpcwlkj.docApi.mapper.DocUserMapper; import com.sxpcwlkj.docApi.mapper.DocUserMapper;
import com.sxpcwlkj.docApi.service.DocUserService; import com.sxpcwlkj.docApi.service.DocUserService;
import com.sxpcwlkj.docApi.utils.NicknameGenerator;
import com.sxpcwlkj.framework.sercice.impl.BaseServiceImpl; import com.sxpcwlkj.framework.sercice.impl.BaseServiceImpl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -22,6 +24,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -107,4 +110,21 @@ public class DocUserServiceImpl extends BaseServiceImpl<DocUser, DocUserVo,DocUs
return wrapper; return wrapper;
} }
@Override
public DocUser bindingOpenId(String openId) {
DocUser docUser= baseMapper.selectOne(new LambdaQueryWrapper<DocUser>().eq(DocUser::getOpenId,openId));
if(docUser!=null){
return docUser;
}else {
docUser= new DocUser();
docUser.setNickname(NicknameGenerator.generateRandomNickname());
docUser.setType("usr");
docUser.setAvatar(DefStaticEnum.MEMBER_DEF_HEADER_IMG.getValue());
docUser.setCtime(new Date());
docUser.setMtime(new Date());
docUser.setOpenId(openId);
baseMapper.insert(docUser);
}
return docUser;
}
} }

View File

@ -1,9 +1,13 @@
package com.sxpcwlkj.docApi.utils; package com.sxpcwlkj.docApi.utils;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWT; import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTHeader; import cn.hutool.jwt.JWTHeader;
import cn.hutool.jwt.JWTUtil; import cn.hutool.jwt.JWTUtil;
import com.sxpcwlkj.common.exception.MmsException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import java.io.Serial; import java.io.Serial;
import java.util.HashMap; import java.util.HashMap;
@ -11,22 +15,26 @@ import java.util.Map;
import static cn.hutool.core.lang.Singleton.put; import static cn.hutool.core.lang.Singleton.put;
@Slf4j
public class DocBaseTool { public class DocBaseTool {
private final String KEY= "4548912314JKJ85HT=="; private final String KEY= "4548912314JKJ85HT==";
public String getUserId(HttpServletRequest request){ public String getUserId(HttpServletRequest request){
String cookie = request.getHeader("cookie"); String cookie = getCookieValue(request,"mss");
if(cookie!=null&&cookie.length()>10){ if(cookie!=null){
cookie=cookie.substring(cookie.indexOf("=")+1); if (StrUtil.isBlank(cookie) || cookie.split("\\.").length != 3) {
log.error("无效的JWT格式: " + cookie);
return "-1";
}
boolean verify = JWTUtil.verify(cookie, KEY.getBytes());
if(verify){
final JWT jwt = JWTUtil.parseToken(cookie);
jwt.getHeader(JWTHeader.TYPE);
return jwt.getPayload("uid").toString();
}
} }
boolean verify = JWTUtil.verify(cookie, KEY.getBytes()); return "-1";
if(verify){
final JWT jwt = JWTUtil.parseToken(cookie);
jwt.getHeader(JWTHeader.TYPE);
return jwt.getPayload("uid").toString();
}
return null;
} }
public String getToken(String uid){ public String getToken(String uid){
@ -42,5 +50,17 @@ public class DocBaseTool {
return JWTUtil.createToken(map, KEY.getBytes()); return JWTUtil.createToken(map, KEY.getBytes());
} }
public String getCookieValue(HttpServletRequest request, String key) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (key.equals(cookie.getName())) {
return cookie.getValue();
}
}
}
return null;
}
} }

View File

@ -7,6 +7,8 @@ import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.util.HashMap;
/** /**
* 返回结果集 * 返回结果集
@ -87,6 +89,17 @@ public class DocR<T> {
return ajaxResult; return ajaxResult;
} }
public static <T> DocR<T> error(String errno, String errmsg) {
DocR<T> ajaxResult = new DocR<>();
ajaxResult.setErrno(errno);
ajaxResult.setErrmsg(errmsg);
ajaxResult.setHost_time(System.currentTimeMillis()+"");
return ajaxResult;
}
} }

View File

@ -0,0 +1,51 @@
package com.sxpcwlkj.docApi.utils;
import java.util.Random;
/**
* @author shanpengnian
*/
public class NicknameGenerator {
// 形容词列表可自行扩展
private static final String[] ADJECTIVES = {
"快乐的", "神秘的", "勇敢的", "聪明的", "优雅的", "顽皮的", "阳光的", "冷静的",
"热情的", "幽默的", "勤奋的", "温柔的", "机灵的", "威武的", "浪漫的", "潇洒的"
};
// 名词列表可自行扩展
private static final String[] NOUNS = {
"熊猫", "狮子", "猎豹", "海豚", "雄鹰", "狐狸", "鲨鱼", "老虎",
"飞鸟", "骏马", "天鹅", "鲸鱼", "蝴蝶", "", "孔雀", "凤凰"
};
public static String generateRandomNickname() {
Random random = new Random();
// 随机选择形容词和名词
String adjective = ADJECTIVES[random.nextInt(ADJECTIVES.length)];
String noun = NOUNS[random.nextInt(NOUNS.length)];
// 随机生成2-4位数字可选部分// 0-2的随机数决定是否添加数字
int numberSuffix = random.nextInt(3);
StringBuilder nickname = new StringBuilder(adjective + noun);
// 50%概率添加数字
if (numberSuffix > 0) {
// 2-4位数字
int digitCount = random.nextInt(3) + 2;
for (int i = 0; i < digitCount; i++) {
// 添加0-9的随机数字
nickname.append(random.nextInt(10));
}
}
return nickname.toString();
}
// 测试方法
public static void main(String[] args) {
// 生成10个示例昵称
for (int i = 0; i < 10; i++) {
System.out.println(generateRandomNickname());
}
}
}

View File

@ -50,17 +50,17 @@ spring:
master: master:
type: ${spring.datasource.type} type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/sxpcwlkj_mms?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true url: jdbc:mysql://localhost:3306/mms?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
username: root username: mms
password: 123456 password: ZKKxz8KGmpGfLGf3
# 从库数据源 # 从库数据源
slave: slave:
lazy: true lazy: true
type: ${spring.datasource.type} type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/sxpcwlkj_mms?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true url: jdbc:mysql://localhost:3306/mms?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
username: root username: mms
password: 123456 password: ZKKxz8KGmpGfLGf3
# oracle: # oracle:
# type: ${spring.datasource.type} # type: ${spring.datasource.type}
# driverClassName: oracle.jdbc.OracleDriver # driverClassName: oracle.jdbc.OracleDriver

View File

@ -22,6 +22,8 @@ public class WxCodeBo {
public static final String REDIS_KEY_LOGIN = "wx:login:"; public static final String REDIS_KEY_LOGIN = "wx:login:";
//Redis key 绑定微信 //Redis key 绑定微信
public static final String REDIS_KEY_BINDING = "wx:binding:"; public static final String REDIS_KEY_BINDING = "wx:binding:";
//Redis key 绑定微信
public static final String REDIS_KEY_DOC_LOGIN = "wx:doc:login:";
private String type; private String type;
@ -61,6 +63,15 @@ public class WxCodeBo {
return this; return this;
} }
public WxCodeBo typeDocLogin() {
this.type = REDIS_KEY_DOC_LOGIN;
this.redisKey = REDIS_KEY_DOC_LOGIN + uuid;
if(this.state==null){
this.state= WxCodeStatusEnum.WAITING.getValue();
}
return this;
}
public WxCodeBo state(WxCodeStatusEnum state) { public WxCodeBo state(WxCodeStatusEnum state) {
this.state = state.getValue(); this.state = state.getValue();
return this; return this;

View File

@ -4,6 +4,8 @@ import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*; import java.io.*;
import java.net.URL; import java.net.URL;
import java.net.URLEncoder; import java.net.URLEncoder;
@ -113,4 +115,17 @@ public class FileUtil extends cn.hutool.core.io.FileUtil {
response.setHeader("download-filename", percentEncodedFileName); response.setHeader("download-filename", percentEncodedFileName);
} }
public static String bufferedImageToBase64(BufferedImage image) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
// 1. 将图像写入字节流
ImageIO.write(image, "PNG", baos);
baos.flush();
// 2. 转换为字节数组并Base64编码
return "data:image/png;base64," +java.util.Base64.getEncoder().encodeToString(baos.toByteArray());
} catch (Exception e) {
throw new RuntimeException("转换失败: " + e.getMessage());
}
}
} }

View File

@ -118,7 +118,7 @@ public class WeChatController {
/** /**
* 微信公众号服务器消息回调 * 微信公众号扫码后的回调
* *
* @param request 请求 * @param request 请求
* @param requestBody body * @param requestBody body
@ -134,7 +134,7 @@ public class WeChatController {
String openid = request.getParameter("openid"); String openid = request.getParameter("openid");
String encType = request.getParameter("encType"); String encType = request.getParameter("encType");
String msgSignature = request.getParameter("msgSignature"); String msgSignature = request.getParameter("msgSignature");
log.info("\n接收到来自微信服务器的认证消息[signature:{}, timestamp:{}, nonce:{}, echostr:{},encType:{},msgSignature:{}]", signature, timestamp, nonce, echostr, encType, msgSignature); log.info("\n接收到来自微信公众号扫码后的回调[signature:{}, timestamp:{}, nonce:{}, echostr:{},encType:{},msgSignature:{}]", signature, timestamp, nonce, echostr, encType, msgSignature);
if (!wxService.getWxMpService().checkSignature(timestamp, nonce, signature)) { if (!wxService.getWxMpService().checkSignature(timestamp, nonce, signature)) {
log.error("【无效的请求】"); log.error("【无效的请求】");
throw new MmsException("无效的请求"); throw new MmsException("无效的请求");
@ -142,12 +142,12 @@ public class WeChatController {
if (encType == null) { if (encType == null) {
// 明文传输的消息 // 明文传输的消息
WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody); WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
log.debug("\n消息内容为\n{} ", inMessage.toString()); log.error("\n消息内容为\n{} ", inMessage.toString());
return wxCodeService.scanCallBack(inMessage); return wxCodeService.scanCallBack(inMessage);
} else if ("aes".equalsIgnoreCase(encType)) { } else if ("aes".equalsIgnoreCase(encType)) {
// aes加密的消息 // aes加密的消息
WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxService.getWxMpService().getWxMpConfigStorage(), timestamp, nonce, msgSignature); WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, wxService.getWxMpService().getWxMpConfigStorage(), timestamp, nonce, msgSignature);
log.debug("\n消息解密后内容为\n{} ", inMessage.toString()); log.error("\n消息解密后内容为\n{} ", inMessage.toString());
return wxCodeService.scanCallBack(inMessage); return wxCodeService.scanCallBack(inMessage);
} }
return ""; return "";

View File

@ -4,6 +4,9 @@ import com.sxpcwlkj.common.utils.R;
import java.util.Map; import java.util.Map;
/**
* @author shanpengnian
*/
public interface WxOrderService { public interface WxOrderService {
/** /**
@ -13,6 +16,13 @@ public interface WxOrderService {
*/ */
R<Object> createPay(Map<String ,Object> orderInfo); R<Object> createPay(Map<String ,Object> orderInfo);
/**
* 验证回调
* @param params 参数
* @return true or false
*/
Boolean verifyNotify(Map<String, String> params);
/** /**
* 查询订单 * 查询订单
* @param orderInfo 订单信息 * @param orderInfo 订单信息

View File

@ -33,10 +33,10 @@ public interface WxService {
/** /**
* 获取微信支付服务 * 获取微信支付服务
* * TradeType.NATIVE.getTradeType()
* @return WxOrderService * @return WxOrderService
*/ */
WxPayService getWxPayService(); WxPayService getWxPayService(String tradeType);

View File

@ -151,7 +151,7 @@ public class WxCodeServiceImpl implements WxCodeService {
*/ */
// 已关注 扫码 SCAN 发生文字 null subscribe unsubscribe event voice text image // 已关注 扫码 SCAN 发生文字 null subscribe unsubscribe event voice text image
log.info("消息类型:{},消息事件:{},发送者账号:{},接收者微信:{},文本消息:{},二维码参数:{}", messageType, messageEvent, fromUser, toUser, text, eventKey); log.info("消息类型:{},消息事件:{},发送者账号:{},接收者微信:{},文本消息:{},二维码参数:{}", messageType, messageEvent, fromUser, toUser, text, businessParams.toJSONString());
WxMpUser wxMpUser = null; WxMpUser wxMpUser = null;
try { try {
wxMpUser = wxService.getWxMpService().getUserService().userInfo(fromUser); wxMpUser = wxService.getWxMpService().getUserService().userInfo(fromUser);

View File

@ -1,11 +1,15 @@
package com.sxpcwlkj.wx.service.impl; package com.sxpcwlkj.wx.service.impl;
import cn.hutool.extra.qrcode.QrCodeUtil;
import com.ijpay.core.enums.SignType; import com.ijpay.core.enums.SignType;
import com.ijpay.core.enums.TradeType; import com.ijpay.core.enums.TradeType;
import com.ijpay.core.kit.QrCodeKit;
import com.ijpay.core.kit.WxPayKit; import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi; import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.WxPayApiConfig; import com.ijpay.wxpay.WxPayApiConfig;
import com.ijpay.wxpay.WxPayApiConfigKit;
import com.ijpay.wxpay.model.UnifiedOrderModel; import com.ijpay.wxpay.model.UnifiedOrderModel;
import com.sxpcwlkj.common.utils.FileUtil;
import com.sxpcwlkj.common.utils.JsonUtil; import com.sxpcwlkj.common.utils.JsonUtil;
import com.sxpcwlkj.common.utils.R; import com.sxpcwlkj.common.utils.R;
import com.sxpcwlkj.wx.config.WxProperties; import com.sxpcwlkj.wx.config.WxProperties;
@ -14,6 +18,7 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.awt.image.BufferedImage;
import java.util.Map; import java.util.Map;
/** /**
@ -26,7 +31,7 @@ public class WxOrderServiceImpl implements WxOrderService {
private final com.github.binarywang.wxpay.service.WxPayService wxPayService; private final com.github.binarywang.wxpay.service.WxPayService wxPayService;
private final WxProperties wxProperties; private final WxProperties wxProperties;
private final WxServiceImpl wxService;
@Override @Override
public R<Object> createPay(Map<String, Object> orderInfo) { public R<Object> createPay(Map<String, Object> orderInfo) {
@ -51,6 +56,7 @@ public class WxOrderServiceImpl implements WxOrderService {
Object tradeType = "JSAPI"; Object tradeType = "JSAPI";
if (orderInfo.containsKey("tradeType")) { if (orderInfo.containsKey("tradeType")) {
tradeType = orderInfo.get("tradeType").toString(); tradeType = orderInfo.get("tradeType").toString();
} }
Object openId = orderInfo.get("openId"); Object openId = orderInfo.get("openId");
@ -59,7 +65,12 @@ public class WxOrderServiceImpl implements WxOrderService {
String payPrice = orderInfo.get("payPrice").toString(); String payPrice = orderInfo.get("payPrice").toString();
Object ip = orderInfo.get("ip"); Object ip = orderInfo.get("ip");
System.out.println("payPrice = " + payPrice); System.out.println("payPrice = " + payPrice);
String notifyUrl=orderInfo.get("notifyUrl").toString();
WxProperties wxProperties = wxService.getWxProperties();
if(wxProperties.getNotifyUrl()!=null){
wxProperties.setNotifyUrl(notifyUrl);
}
WxPayApiConfig wxPayApiConfig =null; WxPayApiConfig wxPayApiConfig =null;
try { try {
@ -68,12 +79,12 @@ public class WxOrderServiceImpl implements WxOrderService {
.mchId(wxProperties.getMchId()) .mchId(wxProperties.getMchId())
.partnerKey(wxProperties.getMchApiKey()) .partnerKey(wxProperties.getMchApiKey())
.certPath(wxProperties.getMchApiKey()) .certPath(wxProperties.getMchApiKey())
// .domain(wxPayBean.getDomain())
.build(); .build();
} catch (Exception e) { } catch (Exception e) {
} }
assert wxPayApiConfig != null;
Map<String, String> params = UnifiedOrderModel Map<String, String> params = UnifiedOrderModel
.builder() .builder()
.appid(wxPayApiConfig.getAppId()) .appid(wxPayApiConfig.getAppId())
@ -81,18 +92,17 @@ public class WxOrderServiceImpl implements WxOrderService {
.nonce_str(WxPayKit.generateStr()) .nonce_str(WxPayKit.generateStr())
.body(productTitle.toString()) .body(productTitle.toString())
.attach(orderNo) .attach(orderNo)
.out_trade_no(WxPayKit.generateStr()) .out_trade_no(orderNo)
.total_fee(payPrice) .total_fee(payPrice)
.spbill_create_ip(ip.toString()) .spbill_create_ip(ip.toString())
.notify_url(wxProperties.getNotifyUrl()) .notify_url(wxProperties.getNotifyUrl())
.trade_type(TradeType.JSAPI.getTradeType()) .trade_type(tradeType.toString())
.openid(openId.toString()) .openid(openId.toString())
.build() .build()
.createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256); .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
String xmlResult = WxPayApi.pushOrder(false, params); String xmlResult = WxPayApi.pushOrder(false, params);
log.info("统一下单:" + xmlResult);
log.info(xmlResult);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult); Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
String returnCode = result.get("return_code"); String returnCode = result.get("return_code");
@ -104,14 +114,22 @@ public class WxOrderServiceImpl implements WxOrderService {
if (!WxPayKit.codeIsOk(resultCode)) { if (!WxPayKit.codeIsOk(resultCode)) {
return R.fail(returnMsg); return R.fail(returnMsg);
} }
// 以下字段在 return_code result_code 都为 SUCCESS 的时候有返回 if (tradeType.equals(TradeType.NATIVE.getTradeType())) {
String prepayId = result.get("prepay_id"); String qrCodeUrl = result.get("code_url");
Map<String, String> packageParams = WxPayKit.miniAppPrepayIdCreateSign(wxPayApiConfig.getAppId(), prepayId, BufferedImage qrCode = QrCodeUtil.generate(qrCodeUrl, 300, 300);
return R.success("生成微信支付二维码成功", FileUtil.bufferedImageToBase64(qrCode));
}
if (tradeType.equals(TradeType.JSAPI.getTradeType())) {
// 以下字段在 return_code result_code 都为 SUCCESS 的时候有返回
String prepayId = result.get("prepay_id");
Map<String, String> packageParams = WxPayKit.miniAppPrepayIdCreateSign(wxPayApiConfig.getAppId(), prepayId,
wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256); wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
String jsonStr = JsonUtil.toJsonString(packageParams); String jsonStr = JsonUtil.toJsonString(packageParams);
log.info("小程序支付的参数:" + jsonStr);
return R.success("调起微信支付成功",packageParams);
}
log.info("小程序支付的参数:" + jsonStr);
return R.success("调起微信支付成功",packageParams);
} catch (Exception e) { } catch (Exception e) {
log.error("createPay error", e); log.error("createPay error", e);
@ -119,6 +137,25 @@ public class WxOrderServiceImpl implements WxOrderService {
return null; return null;
} }
@Override
public Boolean verifyNotify(Map<String, String> params) {
WxProperties wxProperties = wxService.getWxProperties();
WxPayApiConfig wxPayApiConfig =null;
try {
wxPayApiConfig = WxPayApiConfig.builder()
.appId(wxProperties.getAppId())
.mchId(wxProperties.getMchId())
.partnerKey(wxProperties.getMchApiKey())
.certPath(wxProperties.getMchApiKey())
.build();
} catch (Exception e) {
return false;
}
assert wxPayApiConfig != null;
return WxPayKit.verifyNotify(params, wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
}
@Override @Override
public R<Object> selectPay(Map<String, Object> orderInfo) { public R<Object> selectPay(Map<String, Object> orderInfo) {
return null; return null;

View File

@ -133,7 +133,7 @@ public class WxServiceImpl implements WxService {
} }
@Override @Override
public WxPayService getWxPayService() { public WxPayService getWxPayService(String tradeType) {
WxProperties wxProperties = this.getWxProperties(); WxProperties wxProperties = this.getWxProperties();
WxPayConfig payConfig = new WxPayConfig(); WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(wxProperties.getAppId()); payConfig.setAppId(wxProperties.getAppId());
@ -141,7 +141,7 @@ public class WxServiceImpl implements WxService {
payConfig.setMchKey(wxProperties.getMchApiKey()); payConfig.setMchKey(wxProperties.getMchApiKey());
payConfig.setNotifyUrl(wxProperties.getNotifyUrl()); payConfig.setNotifyUrl(wxProperties.getNotifyUrl());
payConfig.setKeyPath(wxProperties.getKeyPath()); payConfig.setKeyPath(wxProperties.getKeyPath());
payConfig.setTradeType("JSAPI"); payConfig.setTradeType(tradeType);
payConfig.setSignType("MD5"); payConfig.setSignType("MD5");
WxPayService wxPayService = new WxPayServiceImpl(); WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig); wxPayService.setConfig(payConfig);

102
script/db/doc-api.sql Normal file
View File

@ -0,0 +1,102 @@
DROP TABLE IF EXISTS doc_user;
CREATE TABLE doc_user(
`uid` VARCHAR(32) NOT NULL COMMENT '用户编号' ,
`nickname` VARCHAR(255) COMMENT '昵称' ,
`avatar` VARCHAR(255) COMMENT '头像' ,
`type` VARCHAR(255) COMMENT '用户类型;usr=普通用户 vip=会员用户' ,
`ctime` DATETIME COMMENT '创建时间' ,
`mtime` DATETIME COMMENT '更新时间' ,
`status` INT DEFAULT 0 COMMENT '状态' ,
`sort` INT DEFAULT 0 COMMENT '排序' ,
`revision` VARCHAR(32) DEFAULT 1 COMMENT '乐观锁' ,
`tenant_id` VARCHAR(32) DEFAULT 000000 COMMENT '租户号' ,
`created_by` VARCHAR(32) COMMENT '创建人' ,
`created_time` DATETIME COMMENT '创建时间' ,
`updated_by` VARCHAR(32) COMMENT '更新人' ,
`updated_time` DATETIME COMMENT '更新时间' ,
`remark` VARCHAR(255) COMMENT '备注' ,
PRIMARY KEY (uid)
) COMMENT = '文档用户;';
DROP TABLE IF EXISTS doc_authorize_user;
CREATE TABLE doc_authorize_user(
`id` VARCHAR(32) NOT NULL COMMENT 'ID' ,
`uid` VARCHAR(32) COMMENT '用户编号' ,
`chan` VARCHAR(255) COMMENT '授权平台' ,
`appid` VARCHAR(255) COMMENT '授权平台标识' ,
`openid` VARCHAR(255) COMMENT '授权平台用户ID' ,
`ctime` DATETIME COMMENT '创建时间' ,
`mtime` DATETIME COMMENT '更新时间' ,
`status` INT DEFAULT 0 COMMENT '状态' ,
`sort` INT DEFAULT 0 COMMENT '排序' ,
`revision` VARCHAR(32) DEFAULT 1 COMMENT '乐观锁' ,
`tenant_id` VARCHAR(32) DEFAULT 000000 COMMENT '租户号' ,
`created_by` VARCHAR(32) COMMENT '创建人' ,
`created_time` DATETIME COMMENT '创建时间' ,
`updated_by` VARCHAR(32) COMMENT '更新人' ,
`updated_time` DATETIME COMMENT '更新时间' ,
`remark` VARCHAR(255) COMMENT '备注' ,
PRIMARY KEY (id)
) COMMENT = '文档授权用户;';
DROP TABLE IF EXISTS doc_product;
CREATE TABLE doc_product(
`prod_id` VARCHAR(255) NOT NULL COMMENT '产品编号' ,
`prod_name` VARCHAR(255) COMMENT '产品名称' ,
`unit_price` VARCHAR(255) COMMENT '销售单价' ,
`mark_price` VARCHAR(255) COMMENT '市场价格' ,
`type` VARCHAR(255) COMMENT '产品类型' ,
`ctime` VARCHAR(255) COMMENT '创建时间' ,
`mtime` VARCHAR(255) COMMENT '更新时间' ,
`status` INT DEFAULT 0 COMMENT '商品状态;up上架 un下降 rm删除' ,
`sort` INT DEFAULT 0 COMMENT '排序' ,
`revision` VARCHAR(32) DEFAULT 1 COMMENT '乐观锁' ,
`tenant_id` VARCHAR(32) DEFAULT 000000 COMMENT '租户号' ,
`created_by` VARCHAR(32) COMMENT '创建人' ,
`created_time` DATETIME COMMENT '创建时间' ,
`updated_by` VARCHAR(32) COMMENT '更新人' ,
`updated_time` DATETIME COMMENT '更新时间' ,
`remark` VARCHAR(255) COMMENT '备注' ,
PRIMARY KEY (prod_id)
) COMMENT = '文档商品;';
DROP TABLE IF EXISTS doc_order;
CREATE TABLE doc_order(
`order_id` VARCHAR(32) NOT NULL COMMENT '订单编号' ,
`uid` VARCHAR(32) COMMENT '用户编号' ,
`txn_amt` DECIMAL(24,2) COMMENT '订单金额' ,
`pay_mchid` VARCHAR(255) COMMENT '支付商户号' ,
`pay_no` VARCHAR(255) COMMENT '支付平台流水号' ,
`pay_timeout` VARCHAR(255) COMMENT '支付超时时间' ,
`prod_id` VARCHAR(255) COMMENT '产品编号' ,
`prod_name` VARCHAR(255) COMMENT '产品名称' ,
`prod_price` DECIMAL(24,2) COMMENT '产品价格' ,
`prod_type` VARCHAR(255) COMMENT '产品类型' ,
`ctime` VARCHAR(255) COMMENT '创建时间' ,
`mtime` VARCHAR(255) COMMENT '更新时间' ,
`status` INT DEFAULT 0 COMMENT '订单状态;unpaid待支付 paysuc已支付 refund已退款 cancel已取消 finish已完成' ,
`sort` INT DEFAULT 0 COMMENT '排序' ,
`revision` VARCHAR(32) DEFAULT 1 COMMENT '乐观锁' ,
`tenant_id` VARCHAR(32) DEFAULT 000000 COMMENT '租户号' ,
`created_by` VARCHAR(32) COMMENT '创建人' ,
`created_time` DATETIME COMMENT '创建时间' ,
`updated_by` VARCHAR(32) COMMENT '更新人' ,
`updated_time` DATETIME COMMENT '更新时间' ,
`remark` VARCHAR(255) COMMENT '备注' ,
PRIMARY KEY (order_id)
) COMMENT = '文档订单;';
DROP TABLE IF EXISTS doc_config;
CREATE TABLE doc_config(
`id` VARCHAR(255) NOT NULL COMMENT 'ID' ,
`key` VARCHAR(255) COMMENT 'KEY' ,
`value` VARCHAR(255) COMMENT '' ,
`ctime` DATETIME COMMENT '创建时间' ,
`mtime` DATETIME COMMENT '更新时间' ,
`status` INT DEFAULT 0 COMMENT '状态' ,
`sort` INT DEFAULT 0 COMMENT '排序' ,
`revision` VARCHAR(32) DEFAULT 1 COMMENT '乐观锁' ,
`tenant_id` VARCHAR(32) DEFAULT 000000 COMMENT '租户号' ,
`created_by` VARCHAR(32) COMMENT '创建人' ,
`created_time` DATETIME COMMENT '创建时间' ,
`updated_by` VARCHAR(32) COMMENT '更新人' ,
`updated_time` DATETIME COMMENT '更新时间' ,
`remark` VARCHAR(255) COMMENT '备注' ,
PRIMARY KEY (id)
) COMMENT = '文档配置;';

View File

@ -4,7 +4,7 @@
"avatar": "", "avatar": "",
"version": "4.9.4", "version": "4.9.4",
"createdTime": "2023-1-5 23:34:04", "createdTime": "2023-1-5 23:34:04",
"updatedTime": "2025-5-28 09:09:44", "updatedTime": "2025-5-28 16:09:59",
"dbConns": [], "dbConns": [],
"profile": { "profile": {
"default": { "default": {
@ -28633,16 +28633,7 @@
"type": "P", "type": "P",
"defName": "文档授权用户", "defName": "文档授权用户",
"notes": {}, "notes": {},
"correlations": [ "correlations": []
{
"myField": "579A3240-EF64-4FEC-A96D-12900732064A",
"refEntity": "D8F9DE2F-D153-4D42-9482-C0D47148DA29",
"refField": "88AF236C-1FF9-45B4-95C7-81CE0F69ACD7",
"myRows": "1",
"refRows": "n",
"innerType": ""
}
]
}, },
{ {
"id": "E7F499AF-F67F-4C1B-A7D2-88940DEAD487", "id": "E7F499AF-F67F-4C1B-A7D2-88940DEAD487",
@ -29164,24 +29155,7 @@
"type": "P", "type": "P",
"defName": "文档订单", "defName": "文档订单",
"notes": {}, "notes": {},
"correlations": [ "correlations": []
{
"myField": "FD3053C8-438F-48A5-9FBC-9C18AFC1A10B",
"refEntity": "58867DE0-2E9E-4C21-9259-6A4D56B2BF9A",
"refField": "2BF94A00-5FF3-47AE-8878-CB38580443AF",
"myRows": "1",
"refRows": "n",
"innerType": ""
},
{
"myField": "AA8BE20F-B9BD-4322-BA6B-3B02E8A26933",
"refEntity": "D8F9DE2F-D153-4D42-9482-C0D47148DA29",
"refField": "88AF236C-1FF9-45B4-95C7-81CE0F69ACD7",
"myRows": "1",
"refRows": "n",
"innerType": ""
}
]
}, },
{ {
"id": "58867DE0-2E9E-4C21-9259-6A4D56B2BF9A", "id": "58867DE0-2E9E-4C21-9259-6A4D56B2BF9A",
@ -31584,22 +31558,6 @@
} }
} }
}, },
{
"id": "ada6c12b-792c-4060-aa54-439cb20fa65a",
"shape": "table",
"position": {
"x": -420,
"y": -100
},
"count": 0,
"originKey": "D8F9DE2F-D153-4D42-9482-C0D47148DA29",
"fillColor": "rgb(51, 153, 108)",
"type": "P",
"size": {
"width": 430,
"height": 238
}
},
{ {
"id": "d0e36814-226b-4212-ad7e-dc8968e0918d", "id": "d0e36814-226b-4212-ad7e-dc8968e0918d",
"shape": "table", "shape": "table",
@ -31615,6 +31573,22 @@
"height": 238 "height": 238
} }
}, },
{
"id": "348961a6-fef4-4871-917c-be6d1d25e4c2",
"shape": "table",
"position": {
"x": -420,
"y": 227
},
"count": 0,
"originKey": "58867DE0-2E9E-4C21-9259-6A4D56B2BF9A",
"fillColor": "rgb(249, 186, 80)",
"type": "P",
"size": {
"width": 430,
"height": 238
}
},
{ {
"id": "f27a8f51-283e-4649-a3e0-38c31c34e19c", "id": "f27a8f51-283e-4649-a3e0-38c31c34e19c",
"shape": "table", "shape": "table",
@ -31647,15 +31621,15 @@
} }
}, },
{ {
"id": "348961a6-fef4-4871-917c-be6d1d25e4c2", "id": "ada6c12b-792c-4060-aa54-439cb20fa65a",
"shape": "table", "shape": "table",
"position": { "position": {
"x": -420, "x": -420,
"y": 227 "y": -100
}, },
"count": 0, "count": 0,
"originKey": "58867DE0-2E9E-4C21-9259-6A4D56B2BF9A", "originKey": "D8F9DE2F-D153-4D42-9482-C0D47148DA29",
"fillColor": "rgb(249, 186, 80)", "fillColor": "rgb(51, 153, 108)",
"type": "P", "type": "P",
"size": { "size": {
"width": 430, "width": 430,