记得上下班打卡 | git大法好,push需谨慎

Commit 8a88d7f4 authored by 姜秀龙's avatar 姜秀龙

Merge remote-tracking branch 'origin/master' into jxl-yirenhebing

parents 099c268d 0b919365
-- 动态表单报名活动 - 字典配置
-- dict_value:英文活动标识,与 C 端提交 activityName、库表 sweet_form_submission.activity_name 一致
-- dict_label:后台展示中文名
-- 可按实际活动增删下方字典数据;重复执行不会重复插入(按 dict_type / dict_value 判重)
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
SELECT '动态表单报名活动', 'sweet_form_activity', '0', 'admin', NOW(), '动态表单报名活动标识与中文名映射'
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type WHERE dict_type = 'sweet_form_activity');
-- 示例字典数据(请按实际活动修改或追加)
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark)
SELECT 1, '2026春季达人招募', 'talent_2026_spring', 'sweet_form_activity', NULL, NULL, 'N', '0', 'admin', NOW(), '示例'
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'sweet_form_activity' AND dict_value = 'talent_2026_spring');
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark)
SELECT 2, '音乐节志愿者报名', 'music_fest_volunteer', 'sweet_form_activity', NULL, NULL, 'N', '0', 'admin', NOW(), '示例'
WHERE NOT EXISTS (SELECT 1 FROM sys_dict_data WHERE dict_type = 'sweet_form_activity' AND dict_value = 'music_fest_volunteer');
-- 动态表单报名活动管理 - 菜单与权限
-- 挂载在「正在映画」顶级菜单下(按 menu_name 查询,兼容线上/测试 menu_id 不一致)
SET @zhengzaiRootId = (
SELECT menu_id
FROM sys_menu
WHERE menu_name = '正在映画'
AND parent_id = 0
AND menu_type = 'M'
LIMIT 1
);
INSERT INTO sys_menu (menu_name, parent_id, order_num, url, target, menu_type, visible, perms, icon, create_by, create_time, update_by, update_time, remark)
VALUES ('报名活动管理', @zhengzaiRootId, 10, '#', 'menuItem', 'M', '0', NULL, 'fa fa-list-alt', 'admin', NOW(), '', NULL, '动态表单报名活动管理');
SET @parentId = LAST_INSERT_ID();
INSERT INTO sys_menu (menu_name, parent_id, order_num, url, target, menu_type, visible, perms, icon, create_by, create_time, update_by, update_time, remark)
VALUES ('活动列表', @parentId, 1, '/sweet/formSubmission/listView', 'menuItem', 'C', '0', 'sweet:formSubmission:list', '#', 'admin', NOW(), '', NULL, '报名活动列表');
SET @menuId = LAST_INSERT_ID();
INSERT INTO sys_menu (menu_name, parent_id, order_num, url, target, menu_type, visible, perms, icon, create_by, create_time, update_by, update_time, remark)
VALUES ('导出', @menuId, 1, '#', '', 'F', '0', 'sweet:formSubmission:export', '#', 'admin', NOW(), '', NULL, '');
package com.liquidnet.service.adam.constant;
public final class AdamTpaConst {
private AdamTpaConst() {
}
/** 国家网络身份认证 */
public static final String PLATFORM_NIA = "NIA";
}
package com.liquidnet.service.adam.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.io.Serializable;
@ApiModel(value = "AdamNiaLoginParam", description = "国家网络身份认证登录入参(R01 凭证认证)")
@Data
public class AdamNiaLoginParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(position = 1, required = true, value = "业务序列号,须与前端 SDK bizSeq 一致[32]")
@NotBlank(message = "bizSeq不能为空")
@Size(min = 32, max = 32, message = "bizSeq长度须为32位")
private String bizSeq;
@ApiModelProperty(position = 2, required = true, value = "认证请求数据 idCardAuthData")
@NotBlank(message = "idCardAuthData不能为空")
private String idCardAuthData;
}
...@@ -22,7 +22,7 @@ public class AdamThirdPartParam implements Serializable { ...@@ -22,7 +22,7 @@ public class AdamThirdPartParam implements Serializable {
@ApiModelProperty(position = 13, required = true, value = "头像[255]", example = "http://pic.zhengzai.tv/default/avatar.png") @ApiModelProperty(position = 13, required = true, value = "头像[255]", example = "http://pic.zhengzai.tv/default/avatar.png")
@Size(max = 255, message = "已超出头像链接长度限制") @Size(max = 255, message = "已超出头像链接长度限制")
private String avatar; private String avatar;
@ApiModelProperty(position = 14, required = true, value = "平台类型[255]", allowableValues = "WEIBO,WECHAT,QQ") @ApiModelProperty(position = 14, required = true, value = "平台类型[255]", allowableValues = "WEIBO,WECHAT,QQ,NIA")
@Pattern(regexp = LnsRegex.Valid.TRIPLE_PF_FOR_ULGOIN, message = "平台类型无效") @Pattern(regexp = LnsRegex.Valid.TRIPLE_PF_FOR_ULGOIN, message = "平台类型无效")
@NotBlank(message = "平台类型不能为空") @NotBlank(message = "平台类型不能为空")
private String platform; private String platform;
......
package com.liquidnet.service.adam.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Getter;
import java.io.Serializable;
/**
* 网证认证成功返回,仅做身份认证不判断绑定。
* 前端拿到 openId 后自行调用 login/tpa 完成登录/注册绑定判断。
*/
@Getter
@Builder
@ApiModel(value = "AdamNiaAuthVo", description = "网证认证结果")
public class AdamNiaAuthVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "平台类型,固定 NIA")
private final String platform;
@ApiModelProperty(value = "网证用户唯一标识(40字节标识的标准Base64,56字符),作为 login/tpa 的 openId 使用")
private final String openId;
}
...@@ -455,6 +455,12 @@ public class GoblinRedisConst { ...@@ -455,6 +455,12 @@ public class GoblinRedisConst {
*/ */
public static final String SQB_GOBLIN_ORDER_SN_KEY = PREFIX.concat("sqb:orderSn:"); public static final String SQB_GOBLIN_ORDER_SN_KEY = PREFIX.concat("sqb:orderSn:");
/**
* 订单退款分布式锁
* {goblin:refund:lock:${orderId}, 1}
*/
public static final String REFUND_ORDER_LOCK = PREFIX.concat("refund:lock:");
/** /**
* 收钱吧 用户订单列表 * 收钱吧 用户订单列表
*/ */
......
...@@ -83,6 +83,8 @@ public class GoblinBackOrderVo implements Serializable, Cloneable { ...@@ -83,6 +83,8 @@ public class GoblinBackOrderVo implements Serializable, Cloneable {
private String auditAt; private String auditAt;
@ApiModelProperty(value = "创建时间") @ApiModelProperty(value = "创建时间")
private String createdAt; private String createdAt;
@ApiModelProperty(value = "发起方[1-用户|2-商家]")
private Integer operationType;
@ApiModelProperty(value = "过期时间") @ApiModelProperty(value = "过期时间")
private String expireAt; private String expireAt;
......
package com.liquidnet.service.goblin.dto.vo;
import com.github.pagehelper.PageInfo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 店铺退款列表接口返回体:分页数据 + 各审核状态 Tab 数量。
*/
@ApiModel("店铺退款列表分页结果")
@Data
public class GoblinStoreBackOrderListPageVo {
/** 当前页列表及分页信息 */
@ApiModelProperty("分页数据")
private PageInfo<GoblinStoreBackOrderListVo> pageInfo;
/** 顶部审核 Tab 角标数量,不受当前 status 筛选影响 */
@ApiModelProperty("各审核状态数量")
private GoblinStoreBackOrderStatusCountVo statusCount;
}
...@@ -20,11 +20,23 @@ public class GoblinStoreBackOrderListVo implements Cloneable { ...@@ -20,11 +20,23 @@ public class GoblinStoreBackOrderListVo implements Cloneable {
private Integer type; private Integer type;
@ApiModelProperty(value = "退款/退货状态[0-商铺发起退款|1-退款申请(用户发送退款请求)|2-退款成功(商家同意退款)|3-退款拒绝(商家拒绝退款)|4-退货申请(用户发起退货请求)|5-退货拒绝(商家拒绝退货)|6-退货审核通过等待用户填写物流(商家审核通过,等待用户寄回商品)|7-待收货(用户已确认)|8-退货完成(商家收货并且同意退款给用户)|9-退货失败(商家不同意退款)|10-退款失败|11-取消退款") @ApiModelProperty(value = "退款/退货状态[0-商铺发起退款|1-退款申请(用户发送退款请求)|2-退款成功(商家同意退款)|3-退款拒绝(商家拒绝退款)|4-退货申请(用户发起退货请求)|5-退货拒绝(商家拒绝退货)|6-退货审核通过等待用户填写物流(商家审核通过,等待用户寄回商品)|7-待收货(用户已确认)|8-退货完成(商家收货并且同意退款给用户)|9-退货失败(商家不同意退款)|10-退款失败|11-取消退款")
private Integer status; private Integer status;
@ApiModelProperty(value = " 退款金额") @ApiModelProperty(value = "退款范围[1-整单退款|2-部分退款]")
private Integer refundScope;
@ApiModelProperty(value = "原订单实付金额")
private BigDecimal priceActual;
@ApiModelProperty(value = "实际退款金额")
private BigDecimal realBackPrice; private BigDecimal realBackPrice;
@ApiModelProperty(value = " 创建时间") @ApiModelProperty(value = "发起方[1-用户发起|2-商家发起]")
private Integer operationType;
@ApiModelProperty(value = "申请时间")
private String createdAt; private String createdAt;
@ApiModelProperty(value = "退款sku") @ApiModelProperty(value = "审核时间")
private String auditAt;
@ApiModelProperty(value = "退款完成时间")
private String refundAt;
@ApiModelProperty(value = "处理时间(优先退款完成时间,其次审核时间,最后拒绝时间)")
private String processedAt;
@ApiModelProperty(value = "退款商品明细")
private List<GoblinBackOrderSkuVo> backOrderSkuVos; private List<GoblinBackOrderSkuVo> backOrderSkuVos;
......
package com.liquidnet.service.goblin.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Map;
/**
* 店铺退款列表 - 各 status 数量。
* status 含义见 {@link GoblinBackOrderVo#getStatus()},前端自行组合 Tab。
*/
@ApiModel("店铺退款列表-各审核状态数量")
@Data
public class GoblinStoreBackOrderStatusCountVo {
@ApiModelProperty("全部")
private long all;
@ApiModelProperty("商铺发起退款[status=0]")
private long storeInitiated;
@ApiModelProperty("退款申请[status=1]")
private long refundApply;
@ApiModelProperty("退款成功[status=2]")
private long refundSuccess;
@ApiModelProperty("退款拒绝[status=3]")
private long refundReject;
@ApiModelProperty("退货申请[status=4]")
private long returnApply;
@ApiModelProperty("退货拒绝[status=5]")
private long returnReject;
@ApiModelProperty("退货审核通过待填物流[status=6]")
private long returnWaitLogistics;
@ApiModelProperty("待收货[status=7]")
private long waitReceive;
@ApiModelProperty("退货完成[status=8]")
private long returnCompleted;
@ApiModelProperty("退货失败[status=9]")
private long returnFailed;
@ApiModelProperty("退款失败[status=10]")
private long refundFailed;
@ApiModelProperty("取消退款[status=11]")
private long refundCancelled;
/**
* 从 Mongo 按 status 分组统计结果组装 VO。
*/
public static GoblinStoreBackOrderStatusCountVo of(long all, Map<Integer, Long> grouped) {
GoblinStoreBackOrderStatusCountVo vo = new GoblinStoreBackOrderStatusCountVo();
vo.all = all;
vo.storeInitiated = grouped.getOrDefault(0, 0L);
vo.refundApply = grouped.getOrDefault(1, 0L);
vo.refundSuccess = grouped.getOrDefault(2, 0L);
vo.refundReject = grouped.getOrDefault(3, 0L);
vo.returnApply = grouped.getOrDefault(4, 0L);
vo.returnReject = grouped.getOrDefault(5, 0L);
vo.returnWaitLogistics = grouped.getOrDefault(6, 0L);
vo.waitReceive = grouped.getOrDefault(7, 0L);
vo.returnCompleted = grouped.getOrDefault(8, 0L);
vo.returnFailed = grouped.getOrDefault(9, 0L);
vo.refundFailed = grouped.getOrDefault(10, 0L);
vo.refundCancelled = grouped.getOrDefault(11, 0L);
return vo;
}
}
package com.liquidnet.service.goblin.dto.vo;
import com.github.pagehelper.PageInfo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 店铺订单列表接口返回体:分页数据 + 各状态 Tab 数量。
*/
@ApiModel("店铺订单列表分页结果")
@Data
public class GoblinStoreOrderListPageVo {
/** 当前页列表及分页信息 */
@ApiModelProperty("分页数据")
private PageInfo<GoblinStoreOrderListVo> pageInfo;
/** 顶部状态 Tab 角标数量,不受当前 status 筛选影响 */
@ApiModelProperty("各状态数量")
private GoblinStoreOrderStatusCountVo statusCount;
}
...@@ -64,6 +64,15 @@ public class GoblinStoreOrderListVo implements Cloneable { ...@@ -64,6 +64,15 @@ public class GoblinStoreOrderListVo implements Cloneable {
@ApiModelProperty(value = " sku相关") @ApiModelProperty(value = " sku相关")
private List<GoblinStoreOrderListSkuVo> storeOrderListSkuVoList; private List<GoblinStoreOrderListSkuVo> storeOrderListSkuVoList;
@ApiModelProperty(value = " 下单账号-UID")
private String userId;
@ApiModelProperty(value = " 下单账号-昵称")
private String userName;
@ApiModelProperty(value = " 下单账号-手机号")
private String userMobile;
private static final GoblinStoreOrderListVo obj = new GoblinStoreOrderListVo(); private static final GoblinStoreOrderListVo obj = new GoblinStoreOrderListVo();
......
package com.liquidnet.service.goblin.dto.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Map;
/**
* 店铺订单列表 - 各状态 Tab 数量。
* 对应前端 statusTabs:全部 / 待支付(0) / 待发货(2) / 已发货(3) / 已完成(4) / 已退款(6)
*/
@ApiModel("店铺订单列表-各状态数量")
@Data
public class GoblinStoreOrderStatusCountVo {
/** 全部(含其他 status,如取消单等) */
@ApiModelProperty("全部")
private long all;
/** 待支付 status=0 */
@ApiModelProperty("待支付[status=0]")
private long pendingPay;
/** 待发货 status=2 */
@ApiModelProperty("待发货[status=2]")
private long pendingShip;
/** 已发货 status=3 */
@ApiModelProperty("已发货[status=3]")
private long shipped;
/** 已完成 status=4 */
@ApiModelProperty("已完成[status=4]")
private long completed;
/** 已退款 status=6 */
@ApiModelProperty("已退款[status=6]")
private long refunded;
/**
* 从 Mongo 按 status 分组统计结果组装 VO。
* 新增 Tab 时在此补充 getOrDefault 即可。
*/
public static GoblinStoreOrderStatusCountVo of(long all, Map<Integer, Long> grouped) {
GoblinStoreOrderStatusCountVo vo = new GoblinStoreOrderStatusCountVo();
vo.all = all;
vo.pendingPay = grouped.getOrDefault(0, 0L);
vo.pendingShip = grouped.getOrDefault(2, 0L);
vo.shipped = grouped.getOrDefault(3, 0L);
vo.completed = grouped.getOrDefault(4, 0L);
vo.refunded = grouped.getOrDefault(6, 0L);
return vo;
}
}
package com.liquidnet.service.goblin.param;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.List;
@Data
public class GoblinStoreOrderRefundParam {
@ApiModelProperty(value = "订单id")
@NotNull(message = "订单id不能为空")
private String orderId;
@ApiModelProperty(value = "退款orderSkuId列表[发货后可选;不传为整单退款;发货前强制整单退款]")
private List<String> orderSkuIds;
@ApiModelProperty(value = "退款原因")
private String reason;
@ApiModelProperty(value = "详细描述")
private String describes;
}
package com.liquidnet.service.goblin.service.manage; package com.liquidnet.service.goblin.service.manage;
import com.github.pagehelper.PageInfo;
import com.liquidnet.service.base.ResponseDto; import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinBackOrderDetailsVo; import com.liquidnet.service.goblin.dto.vo.GoblinBackOrderDetailsVo;
import com.liquidnet.service.goblin.dto.vo.GoblinBackOrderVo; import com.liquidnet.service.goblin.dto.vo.GoblinStoreBackOrderListPageVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreBackOrderListVo; import com.liquidnet.service.goblin.dto.vo.GoblinStoreBackOrderListVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreOrderListVo; import com.liquidnet.service.goblin.param.GoblinStoreOrderRefundParam;
import com.liquidnet.service.goblin.param.RefundCallbackParam;
import java.math.BigDecimal; import java.math.BigDecimal;
public interface IGoblinStoreBackOrderService { public interface IGoblinStoreBackOrderService {
ResponseDto<PageInfo<GoblinStoreBackOrderListVo>> orderBackList(Integer page, ResponseDto<GoblinStoreBackOrderListPageVo> orderBackList(Integer page,
String orderBackCode, String orderBackCode,
Integer type, Integer type,
String cst, String cst,
String cet, String cet,
String orderCode, String orderCode,
String spuName, String spuName,
Integer status); String status);
ResponseDto<GoblinBackOrderDetailsVo> orderDetails(String backOrderId); ResponseDto<GoblinBackOrderDetailsVo> orderDetails(String backOrderId);
...@@ -28,5 +26,7 @@ public interface IGoblinStoreBackOrderService { ...@@ -28,5 +26,7 @@ public interface IGoblinStoreBackOrderService {
ResponseDto<Boolean> refusedRefund(String backOrderId); ResponseDto<Boolean> refusedRefund(String backOrderId);
ResponseDto<Boolean> refundOrder(GoblinStoreOrderRefundParam param);
ResponseDto<Boolean> changeSkuRefund(String backOrderId, BigDecimal refundPrice, String orderSkuId); ResponseDto<Boolean> changeSkuRefund(String backOrderId, BigDecimal refundPrice, String orderSkuId);
} }
package com.liquidnet.service.goblin.service.manage; package com.liquidnet.service.goblin.service.manage;
import com.github.pagehelper.PageInfo;
import com.liquidnet.service.base.ResponseDto; import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinOrderLogVo; import com.liquidnet.service.goblin.dto.vo.GoblinStoreOrderListPageVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreOrderListVo; import com.liquidnet.service.goblin.dto.vo.GoblinStoreOrderListVo;
import com.liquidnet.service.goblin.param.RefundCallbackParam;
import org.springframework.web.bind.annotation.RequestParam;
import javax.validation.Valid; import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal; import java.math.BigDecimal;
public interface IGoblinStoreOrderService { public interface IGoblinStoreOrderService {
ResponseDto<PageInfo<GoblinStoreOrderListVo>> orderList(Integer page, ResponseDto<GoblinStoreOrderListPageVo> orderList(Integer page,
String orderCode, String orderCode,
Integer type, Integer type,
String cst, String cst,
...@@ -20,6 +17,14 @@ public interface IGoblinStoreOrderService { ...@@ -20,6 +17,14 @@ public interface IGoblinStoreOrderService {
String phone, String phone,
Integer status); Integer status);
void exportOrderList(HttpServletResponse response,
String orderCode,
String cst,
String cet,
String expressContacts,
String phone,
Integer status);
ResponseDto<GoblinStoreOrderListVo> orderDetails(String orderId); ResponseDto<GoblinStoreOrderListVo> orderDetails(String orderId);
ResponseDto<Boolean> orderCancel(String orderId); ResponseDto<Boolean> orderCancel(String orderId);
...@@ -32,8 +37,8 @@ public interface IGoblinStoreOrderService { ...@@ -32,8 +37,8 @@ public interface IGoblinStoreOrderService {
ResponseDto<Boolean> refundOrderSku(String orderId, String orderSkuId, BigDecimal price); ResponseDto<Boolean> refundOrderSku(String orderId, String orderSkuId, BigDecimal price);
ResponseDto<Boolean> express(String orderId, String orderSkuIds,String mailNo,String uid,String orderCode); ResponseDto<Boolean> express(String orderId, String orderSkuIds, String mailNo, String uid, String orderCode);
ResponseDto<Boolean> changeExpressMailNo(String orderId, String mailId,String mailNo); ResponseDto<Boolean> changeExpressMailNo(String orderId, String mailId, String mailNo);
} }
package com.liquidnet.service.sweet.dto.param.admin;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel(value = "SweetFormSubmissionActivityListParam", description = "动态表单报名活动列表查询")
public class SweetFormSubmissionActivityListParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "活动标识(英文)")
private String activityName;
}
package com.liquidnet.service.sweet.dto.param.admin;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
@Data
@ApiModel(value = "SweetFormSubmissionExportParam", description = "动态表单报名导出")
public class SweetFormSubmissionExportParam implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "活动标识(英文)", required = true)
private String activityName;
@ApiModelProperty(value = "开始时间 yyyy-MM-dd HH:mm:ss")
private String beginTime;
@ApiModelProperty(value = "结束时间 yyyy-MM-dd HH:mm:ss")
private String endTime;
}
...@@ -47,7 +47,13 @@ public class CommonController ...@@ -47,7 +47,13 @@ public class CommonController
{ {
throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName)); throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
} }
String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); int underscoreIndex = fileName.indexOf("_");
String realFileName;
if (underscoreIndex > 0) {
realFileName = System.currentTimeMillis() + fileName.substring(underscoreIndex + 1);
} else {
realFileName = fileName;
}
String filePath = RuoYiConfig.getDownloadPath() + fileName; String filePath = RuoYiConfig.getDownloadPath() + fileName;
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
......
package com.liquidnet.client.admin.web.controller.zhengzai.sweet;
import com.github.pagehelper.PageHelper;
import com.liquidnet.client.admin.common.annotation.Log;
import com.liquidnet.client.admin.common.core.controller.BaseController;
import com.liquidnet.client.admin.common.core.domain.AjaxResult;
import com.liquidnet.client.admin.common.core.page.PageDomain;
import com.liquidnet.client.admin.common.core.page.TableDataInfo;
import com.liquidnet.client.admin.common.core.page.TableSupport;
import com.liquidnet.client.admin.common.enums.BusinessType;
import com.liquidnet.client.admin.common.utils.StringUtils;
import com.liquidnet.client.admin.zhengzai.sweet.service.ISweetFormSubmissionAdminService;
import com.liquidnet.service.sweet.dto.param.admin.SweetFormSubmissionActivityListParam;
import com.liquidnet.service.sweet.dto.param.admin.SweetFormSubmissionExportParam;
import com.liquidnet.service.sweet.dto.admin.SweetFormSubmissionActivityVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Api(tags = "动态表单报名活动管理")
@Controller
@RequestMapping("/sweet/formSubmission")
public class SweetFormSubmissionAdminController extends BaseController {
private String prefix = "zhengzai/sweet/formSubmission";
@Autowired
private ISweetFormSubmissionAdminService sweetFormSubmissionAdminService;
@GetMapping("listView")
@ApiOperation(value = "报名活动列表页面")
public String listView() {
return prefix + "/list";
}
@Log(title = "报名活动列表", businessType = BusinessType.LIST)
@RequiresPermissions("sweet:formSubmission:list")
@PostMapping("list")
@ApiOperation(value = "报名活动列表数据")
@ResponseBody
public TableDataInfo list(SweetFormSubmissionActivityListParam param) {
PageDomain pageDomain = TableSupport.buildPageRequest();
Integer pageNum = pageDomain.getPageNum();
Integer pageSize = pageDomain.getPageSize();
if (StringUtils.isNull(pageNum)) {
pageNum = 1;
}
if (StringUtils.isNull(pageSize)) {
pageSize = 10;
}
// 聚合查询排序在 SQL 中固定,忽略前端 orderByColumn 避免 PageHelper 追加无效字段
PageHelper.startPage(pageNum, pageSize);
List<SweetFormSubmissionActivityVo> list = sweetFormSubmissionAdminService.activityList(param);
return getDataTable(list);
}
@Log(title = "报名数据导出", businessType = BusinessType.EXPORT)
@RequiresPermissions("sweet:formSubmission:export")
@PostMapping("export")
@ApiOperation(value = "报名数据导出")
@ResponseBody
public AjaxResult export(SweetFormSubmissionExportParam param) {
return sweetFormSubmissionAdminService.export(param);
}
}
<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<th:block th:include="include :: header('报名活动列表')"/>
</head>
<body class="gray-bg">
<div class="container-div">
<div class="row">
<div class="col-sm-12 search-collapse">
<form id="formId">
<div class="select-list">
<ul>
<li>
活动:<select name="activityName" th:with="type=${@dict.getType('sweet_form_activity')}">
<option value="">全部</option>
<option th:each="dict : ${type}" th:text="${dict.dictLabel}"
th:value="${dict.dictValue}"></option>
</select>
</li>
<li>
<a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i
class="fa fa-search"></i>&nbsp;搜索</a>
<a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i
class="fa fa-refresh"></i>&nbsp;重置</a>
</li>
</ul>
</div>
</form>
</div>
<div class="col-sm-12 select-table table-bordered">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
<th:block th:include="include :: footer"/>
<script th:inline="javascript">
var exportFlag = [[${@permission.hasPermi('sweet:formSubmission:export')}]];
var prefix = ctx + "sweet/formSubmission";
function formatDateTime(value) {
return $.common.sprintf("<span>%s</span>", null != value ? value.replace("T", " ").substring(0, 19) : value);
}
function exportAll(activityName) {
doExport({activityName: activityName});
}
function exportByTime(activityName) {
var html = '<div style="padding: 20px;">' +
'<div class="form-group">' +
'<label>开始时间:</label>' +
'<input type="text" class="time-input form-control" id="exportBeginTime" placeholder="yyyy-MM-dd HH:mm:ss"/>' +
'</div>' +
'<div class="form-group">' +
'<label>结束时间:</label>' +
'<input type="text" class="time-input form-control" id="exportEndTime" placeholder="yyyy-MM-dd HH:mm:ss"/>' +
'</div>' +
'</div>';
layer.open({
type: 1,
title: '按时间导出',
area: ['420px', '260px'],
content: html,
btn: ['导出', '取消'],
success: function () {
layui.use('laydate', function () {
var laydate = layui.laydate;
laydate.render({elem: '#exportBeginTime', type: 'datetime', trigger: 'click'});
laydate.render({elem: '#exportEndTime', type: 'datetime', trigger: 'click'});
});
},
yes: function (index) {
var beginTime = $('#exportBeginTime').val();
var endTime = $('#exportEndTime').val();
if (!beginTime && !endTime) {
$.modal.msgWarning('请至少填写一个时间');
return;
}
layer.close(index);
doExport({activityName: activityName, beginTime: beginTime, endTime: endTime});
}
});
}
function doExport(data) {
$.modal.loading("正在导出数据,请稍后...");
$.post(prefix + "/export", data, function (result) {
$.modal.closeLoading();
if (result.code == web_status.SUCCESS) {
window.location.href = ctx + "common/download?fileName=" + encodeURI(result.msg) + "&delete=" + true;
} else {
$.modal.alertError(result.msg);
}
});
}
$(function () {
var options = {
url: prefix + "/list",
modalName: "报名活动",
sortable: false,
columns: [
{
field: 'activityName',
title: '活动标识(英文)'
},
{
field: 'activityLabel',
title: '活动名称(中文)',
formatter: function (value, row) {
return value || row.activityName;
}
},
{
field: 'submissionCount',
title: '报名数量'
},
{
field: 'firstCreatedAt',
title: '最早提交',
formatter: function (value) {
return formatDateTime(value);
}
},
{
field: 'lastCreatedAt',
title: '最新提交',
formatter: function (value) {
return formatDateTime(value);
}
},
{
title: '操作',
align: 'center',
formatter: function (value, row) {
var actions = [];
actions.push('<a class="btn btn-warning btn-xs ' + exportFlag + '" href="javascript:void(0)" onclick="exportAll(\'' + row.activityName + '\')"><i class="fa fa-download"></i> 全部导出</a>');
actions.push('<a class="btn btn-info btn-xs ' + exportFlag + '" href="javascript:void(0)" onclick="exportByTime(\'' + row.activityName + '\')"><i class="fa fa-clock-o"></i> 按时间导出</a>');
return actions.join(' ');
}
}
]
};
$.table.init(options);
});
</script>
</body>
</html>
package com.liquidnet.client.admin.zhengzai.sweet.service;
import com.liquidnet.client.admin.common.core.domain.AjaxResult;
import com.liquidnet.service.sweet.dto.param.admin.SweetFormSubmissionActivityListParam;
import com.liquidnet.service.sweet.dto.param.admin.SweetFormSubmissionExportParam;
import com.liquidnet.service.sweet.dto.admin.SweetFormSubmissionActivityVo;
import java.util.List;
public interface ISweetFormSubmissionAdminService {
List<SweetFormSubmissionActivityVo> activityList(SweetFormSubmissionActivityListParam param);
AjaxResult export(SweetFormSubmissionExportParam param);
}
package com.liquidnet.client.admin.zhengzai.sweet.service.impl;
import com.liquidnet.client.admin.common.core.domain.AjaxResult;
import com.liquidnet.client.admin.common.core.domain.entity.SysDictData;
import com.liquidnet.client.admin.common.utils.DictUtils;
import com.liquidnet.client.admin.common.utils.StringUtils;
import com.liquidnet.client.admin.zhengzai.sweet.service.ISweetFormSubmissionAdminService;
import com.liquidnet.client.admin.zhengzai.sweet.utils.SweetFormSubmissionExcelUtils;
import com.liquidnet.service.sweet.dto.param.admin.SweetFormSubmissionActivityListParam;
import com.liquidnet.service.sweet.dto.param.admin.SweetFormSubmissionExportParam;
import com.liquidnet.service.sweet.dto.admin.SweetFormSubmissionActivityVo;
import com.liquidnet.service.sweet.entity.SweetFormSubmission;
import com.liquidnet.service.sweet.mapper.SweetFormSubmissionMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class SweetFormSubmissionAdminServiceImpl implements ISweetFormSubmissionAdminService {
public static final String DICT_TYPE_SWEET_FORM_ACTIVITY = "sweet_form_activity";
@Autowired
private SweetFormSubmissionMapper sweetFormSubmissionMapper;
@Override
public List<SweetFormSubmissionActivityVo> activityList(SweetFormSubmissionActivityListParam param) {
String activityName = StringUtils.isEmpty(param.getActivityName()) ? null : param.getActivityName();
List<SweetFormSubmissionActivityVo> list = sweetFormSubmissionMapper.selectActivityGroupList(activityName);
for (SweetFormSubmissionActivityVo vo : list) {
vo.setActivityLabel(getActivityLabel(vo.getActivityName()));
}
return list;
}
private String getActivityLabel(String activityName) {
if (StringUtils.isEmpty(activityName)) {
return "";
}
List<SysDictData> datas = DictUtils.getDictCache(DICT_TYPE_SWEET_FORM_ACTIVITY);
if (datas != null) {
for (SysDictData dict : datas) {
if (activityName.equals(dict.getDictValue())) {
return dict.getDictLabel();
}
}
}
return activityName;
}
@Override
public AjaxResult export(SweetFormSubmissionExportParam param) {
if (StringUtils.isEmpty(param.getActivityName())) {
return AjaxResult.error("请选择活动");
}
String beginTime = StringUtils.isEmpty(param.getBeginTime()) ? null : param.getBeginTime();
String endTime = StringUtils.isEmpty(param.getEndTime()) ? null : param.getEndTime();
List<SweetFormSubmission> submissions = sweetFormSubmissionMapper.selectForExport(
param.getActivityName(), beginTime, endTime);
String activityLabel = getActivityLabel(param.getActivityName());
String exportName = SweetFormSubmissionExcelUtils.buildExportName(activityLabel, beginTime, endTime);
String filename = SweetFormSubmissionExcelUtils.export(submissions, exportName);
return AjaxResult.success(filename);
}
}
package com.liquidnet.client.admin.zhengzai.sweet.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.liquidnet.client.admin.common.config.RuoYiConfig;
import com.liquidnet.client.admin.common.exception.BusinessException;
import com.liquidnet.client.admin.common.utils.StringUtils;
import com.liquidnet.service.sweet.entity.SweetFormSubmission;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.File;
import java.io.FileOutputStream;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 动态表单报名 Excel 导出
*/
public class SweetFormSubmissionExcelUtils {
private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final String COL_CREATED_AT = "提交时间";
private SweetFormSubmissionExcelUtils() {
}
public static String export(List<SweetFormSubmission> submissions, String exportName) {
if (submissions == null || submissions.isEmpty()) {
throw new BusinessException("查无报名信息");
}
List<Map<String, String>> rows = new ArrayList<>();
Set<String> dataKeys = new LinkedHashSet<>();
for (SweetFormSubmission submission : submissions) {
Map<String, String> row = new LinkedHashMap<>();
row.put(COL_CREATED_AT, submission.getCreatedAt() == null ? "" : submission.getCreatedAt().format(DTF));
parseDataJson(submission.getData(), row, dataKeys);
rows.add(row);
}
List<String> headers = new ArrayList<>();
headers.add(COL_CREATED_AT);
headers.addAll(dataKeys);
String sheetName = sanitizeSheetName(exportName);
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet(sheetName);
Row headerRow = sheet.createRow(0);
for (int i = 0; i < headers.size(); i++) {
headerRow.createCell(i).setCellValue(headers.get(i));
}
int rowNum = 1;
for (Map<String, String> rowData : rows) {
Row row = sheet.createRow(rowNum++);
for (int i = 0; i < headers.size(); i++) {
String value = rowData.get(headers.get(i));
row.createCell(i).setCellValue(value == null ? "" : value);
}
}
String filename = sanitizeFilename(exportName) + ".xlsx";
String absolutePath = RuoYiConfig.getDownloadPath() + filename;
File desc = new File(absolutePath);
if (!desc.getParentFile().exists()) {
desc.getParentFile().mkdirs();
}
try (FileOutputStream out = new FileOutputStream(desc)) {
workbook.write(out);
workbook.close();
} catch (Exception e) {
throw new BusinessException("导出Excel失败,请联系网站管理员!");
}
return filename;
}
public static String buildExportName(String activityLabel, String beginTime, String endTime) {
StringBuilder name = new StringBuilder(activityLabel).append("报名数据");
if (StringUtils.isNotEmpty(beginTime) || StringUtils.isNotEmpty(endTime)) {
name.append("_");
if (StringUtils.isNotEmpty(beginTime)) {
name.append(normalizeTimeForName(beginTime));
}
name.append("-");
if (StringUtils.isNotEmpty(endTime)) {
name.append(normalizeTimeForName(endTime));
}
}
return name.toString();
}
private static String normalizeTimeForName(String time) {
return time.trim().replace(":", "").replace(" ", "_");
}
private static String sanitizeSheetName(String name) {
if (StringUtils.isEmpty(name)) {
return "报名数据";
}
String sanitized = name.replaceAll("[\\\\/?*\\[\\]:]", "_");
return sanitized.length() > 31 ? sanitized.substring(0, 31) : sanitized;
}
private static String sanitizeFilename(String name) {
if (StringUtils.isEmpty(name)) {
return "报名数据";
}
return name.replaceAll("[\\\\/:*?\"<>|]", "_");
}
private static void parseDataJson(String dataJson, Map<String, String> row, Set<String> dataKeys) {
if (dataJson == null || dataJson.trim().isEmpty()) {
return;
}
try {
JSONObject jsonObject = JSON.parseObject(dataJson, Feature.OrderedField);
if (jsonObject == null) {
return;
}
for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
String key = entry.getKey();
if (!dataKeys.contains(key)) {
dataKeys.add(key);
}
row.put(key, formatValue(entry.getValue()));
}
} catch (Exception ignored) {
if (!dataKeys.contains("data")) {
dataKeys.add("data");
}
row.put("data", dataJson);
}
}
private static String formatValue(Object value) {
if (value == null) {
return "";
}
if (value instanceof Map || value instanceof List) {
return JSON.toJSONString(value);
}
return String.valueOf(value);
}
}
...@@ -119,7 +119,7 @@ public class LnsRegex { ...@@ -119,7 +119,7 @@ public class LnsRegex {
/** /**
* 支持的第三方账号平台类型(用户中心:登录注册) * 支持的第三方账号平台类型(用户中心:登录注册)
*/ */
public static final String TRIPLE_PF_FOR_ULGOIN = "\\b(WEIBO|WECHAT|QQ)\\b"; public static final String TRIPLE_PF_FOR_ULGOIN = "\\b(WEIBO|WECHAT|QQ|NIA)\\b";
/** /**
* 支持的支付终端 * 支持的支付终端
*/ */
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-common-third</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>liquidnet-common-third-secure-access</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-common-base</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>cn.anicert.module</groupId>
<artifactId>anicert-sign-bouncycastle</artifactId>
<version>3.2.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/anicert-sign-bouncycastle-3.2.0.jar</systemPath>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.26</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-setting</artifactId>
<version>5.8.26</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-log</artifactId>
<version>5.8.26</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.68</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.68</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
</project>
package cn.anicert.secure.access.bean;
import java.io.Serializable;
public class SARequestPackageVO implements Serializable {
private static final long serialVersionUID = 1L;
private Object bizPackage;
private String customerNo;
private String forwardUrl;
public SARequestPackageVO() {
}
public SARequestPackageVO(Object bizPackage, String customerNo, String forwardUrl) {
this.bizPackage = bizPackage;
this.customerNo = customerNo;
this.forwardUrl = forwardUrl;
}
public Object getBizPackage() {
return bizPackage;
}
public void setBizPackage(Object bizPackage) {
this.bizPackage = bizPackage;
}
public String getCustomerNo() {
return customerNo;
}
public void setCustomerNo(String customerNo) {
this.customerNo = customerNo;
}
public String getForwardUrl() {
return forwardUrl;
}
public void setForwardUrl(String forwardUrl) {
this.forwardUrl = forwardUrl;
}
}
package cn.anicert.secure.access.bean;
import java.io.Serializable;
public class SARequestVO implements Serializable {
private static final long serialVersionUID = 1L;
private Object requestPackage;
private String requestSign;
public SARequestVO() {
}
public SARequestVO(Object requestPackage, String requestSign) {
this.requestPackage = requestPackage;
this.requestSign = requestSign;
}
public Object getRequestPackage() {
return requestPackage;
}
public void setRequestPackage(Object requestPackage) {
this.requestPackage = requestPackage;
}
public String getRequestSign() {
return requestSign;
}
public void setRequestSign(String requestSign) {
this.requestSign = requestSign;
}
}
package cn.anicert.secure.access.bean;
import java.io.Serializable;
public class SAResponseVO implements Serializable {
private static final long serialVersionUID = 1L;
private Object responsePackage;
private String responseSign;
public SAResponseVO() {
}
public SAResponseVO(Object responsePackage, String responseSign) {
this.responsePackage = responsePackage;
this.responseSign = responseSign;
}
public Object getResponsePackage() {
return responsePackage;
}
public void setResponsePackage(Object responsePackage) {
this.responsePackage = responsePackage;
}
public String getResponseSign() {
return responseSign;
}
public void setResponseSign(String responseSign) {
this.responseSign = responseSign;
}
}
package cn.anicert.secure.access.bean;
import java.io.Serializable;
public class SASignRequestPackageVO implements Serializable {
private static final long serialVersionUID = 1L;
private String customerNo;
private String originalData;
public SASignRequestPackageVO() {
}
public SASignRequestPackageVO(String customerNo, String originalData) {
this.customerNo = customerNo;
this.originalData = originalData;
}
public String getCustomerNo() {
return customerNo;
}
public void setCustomerNo(String customerNo) {
this.customerNo = customerNo;
}
public String getOriginalData() {
return originalData;
}
public void setOriginalData(String originalData) {
this.originalData = originalData;
}
}
package cn.anicert.secure.access.config;
import cn.anicert.secure.access.config.bean.AccessConfig;
import cn.anicert.secure.access.enums.Configs;
import cn.anicert.secure.access.exceptions.SecureAccessConfigException;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.StaticLog;
import cn.hutool.setting.Setting;
import cn.hutool.setting.SettingUtil;
import java.util.Arrays;
public class ConfigReader {
private final static String CONFIG_FILE_NAME = "secure-access.setting";
private static Setting setting;
public static AccessConfig readConfig(String... filePath) {
if (filePath == null || filePath.length == 0) {
setting = SettingUtil.getFirstFound(CONFIG_FILE_NAME);
} else {
setting = SettingUtil.getFirstFound(filePath);
}
StaticLog.info("[安全接入平台]当前配置文件路径为:{}", setting.getSettingPath());
StaticLog.info("[安全接入平台]开始读取配置文件…………");
AccessConfig accessConfig = new AccessConfig();
Arrays.stream(Configs.values()).forEach(config -> {
if (config.isNecessary()) {
if (setting.containsKey(config.getKey())) {
String value = setting.getStr(config.getKey());
if (StrUtil.isBlank(value)) {
StaticLog.error("[安全接入平台]{}未配置", config.getName());
throwConfigException();
}
} else {
StaticLog.error("[安全接入平台]{}未配置", config.getName());
throwConfigException();
}
}
});
setting.autoLoad(true);
setting.toBean(accessConfig);
StaticLog.info("[安全接入平台]读取配置文件结束");
return accessConfig;
}
public static AccessConfig writeConfig(String key, String value) {
setting.set(key, value);
setting.store();
AccessConfig accessConfig = new AccessConfig();
return setting.toBean(accessConfig);
}
private static void throwConfigException() {
throw new SecureAccessConfigException("[安全接入平台]读取配置文件失败");
}
}
package cn.anicert.secure.access.config.bean;
public class AccessConfig {
//配置部分
private String orgId;
private String customerNo;
private String accessUrl;
private String signPrivateKey;
private String signVerifyPublicKey;
private String dataEncryptKey;
//请求部分
private int connectionRequestTimeout = 50000;
private int responseTimeout = 30000;
private int poolMaxConn = 100;
public String getCustomerNo() {
return customerNo;
}
public void setCustomerNo(String customerNo) {
this.customerNo = customerNo;
}
public String getAccessUrl() {
return accessUrl;
}
public void setAccessUrl(String accessUrl) {
this.accessUrl = accessUrl;
}
public String getSignPrivateKey() {
return signPrivateKey;
}
public void setSignPrivateKey(String signPrivateKey) {
this.signPrivateKey = signPrivateKey;
}
public String getSignVerifyPublicKey() {
return signVerifyPublicKey;
}
public void setSignVerifyPublicKey(String signVerifyPublicKey) {
this.signVerifyPublicKey = signVerifyPublicKey;
}
public String getDataEncryptKey() {
return dataEncryptKey;
}
public void setDataEncryptKey(String dataEncryptKey) {
this.dataEncryptKey = dataEncryptKey;
}
public int getConnectionRequestTimeout() {
return connectionRequestTimeout;
}
public void setConnectionRequestTimeout(int connectionRequestTimeout) {
this.connectionRequestTimeout = connectionRequestTimeout;
}
public int getResponseTimeout() {
return responseTimeout;
}
public void setResponseTimeout(int responseTimeout) {
this.responseTimeout = responseTimeout;
}
public int getPoolMaxConn() {
return poolMaxConn;
}
public void setPoolMaxConn(int poolMaxConn) {
this.poolMaxConn = poolMaxConn;
}
public String getOrgId() {
return orgId;
}
public void setOrgId(String orgId) {
this.orgId = orgId;
}
}
package cn.anicert.secure.access.constants;
public class InterfaceConstant {
private static final String BASE_URL = "/secureaccess/interf";
public static final String UNIFIED_AUTH_URL = BASE_URL + "/unified_auth/request";
public static final String P7_SIGN_URL = BASE_URL + "/pkcs7/signDataByP7";
}
package cn.anicert.secure.access.core;
import cn.anicert.secure.access.bean.SAResponseVO;
import cn.anicert.secure.access.constants.InterfaceConstant;
import cn.anicert.secure.access.utils.ConfigUtil;
import cn.anicert.secure.access.utils.HttpUtil;
import cn.anicert.secure.access.utils.JSONUtil;
import cn.anicert.secure.access.utils.SignUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.StaticLog;
public class SecureAccess {
public static void init(String... configPath) {
ConfigUtil.initConfig(configPath);
}
public static String unified_auth(String bizSeq, String reqStr) {
String url = ConfigUtil.getConfig().getAccessUrl() + InterfaceConstant.UNIFIED_AUTH_URL;
String responseStr = sendReq(bizSeq, url, reqStr);
if (StrUtil.isNotBlank(responseStr) && verifySign(bizSeq, responseStr)) {
return dealResult(bizSeq, responseStr);
}
return "";
}
public static String pkcs7Sign(String bizSeq, String reqStr) {
String url = ConfigUtil.getConfig().getAccessUrl() + InterfaceConstant.P7_SIGN_URL;
String responseStr = sendReq(bizSeq, url, reqStr);
if (StrUtil.isNotBlank(responseStr) && verifySign(bizSeq, responseStr)) {
return dealResult(bizSeq, responseStr);
}
return "";
}
private static String sendReq(String bizSeq, String url, String reqStr) {
try {
StaticLog.info("[安全接入平台],bizSeq--[{}]--请求开始", bizSeq);
StaticLog.info("[安全接入平台],bizSeq--[{}]--请求数据--[{}]", bizSeq, reqStr);
String responseStr = HttpUtil.getInstance().postRequest(url, reqStr);
if (StrUtil.isNotBlank(responseStr)) {
StaticLog.info("[安全接入平台],bizSeq--[{}]--请求结果--[{}]", bizSeq, responseStr);
return responseStr;
} else {
StaticLog.error("[安全接入平台],bizSeq--[{}]--请求返回结果为空", bizSeq);
}
} catch (Exception e) {
StaticLog.error(e, "[安全接入平台],bizSeq--[{}]--请求失败", bizSeq);
}
return "";
}
private static boolean verifySign(String bizSeq, String result) {
try {
SAResponseVO responseVO = JSONUtil.json2Object(result, SAResponseVO.class);
if (StrUtil.isNotBlank(responseVO.getResponseSign())) {
String signStr = JSONUtil.toJson(responseVO.getResponsePackage());
boolean verifyResult = SignUtil.verifySign(signStr, responseVO.getResponseSign());
if (!verifyResult) {
StaticLog.error("[安全接入平台],bizSeq--[{}]--验签不匹配", bizSeq);
}
return verifyResult;
} else {
StaticLog.error("[安全接入平台],bizSeq--[{}]--验签报文体为空", bizSeq);
return false;
}
} catch (Exception e) {
StaticLog.error(e, "[安全接入平台],bizSeq--[{}]--验签异常", bizSeq);
return false;
}
}
private static String dealResult(String bizSeq, String result) {
try {
SAResponseVO responseVO = JSONUtil.json2Object(result, SAResponseVO.class);
return JSONUtil.toJson(responseVO.getResponsePackage());
} catch (Exception e) {
StaticLog.error(e, "[安全接入平台],bizSeq--[{}]--处理返回报文异常", bizSeq);
return "";
}
}
}
package cn.anicert.secure.access.crypto.service;
import javax.security.auth.Subject;
/**
* @Description
* @Date 2024/5/29 15:30
* @Author yourongbin
**/
public interface ISoftCryptoService {
/**
* 数字信封加密
* @param cert 证书的base64编码
* @param data 加密数据
* @return
*/
public String encodeEnvelop(String cert, String data) throws Exception ;
/**
* 数字信封加密
* @param cert 证书的base64编码
* @param dataByte
* @return
*/
public String encodeEnvelop(String cert, byte[] dataByte) throws Exception;
/**
* 数字信封解密 返回明文
* @param priKey
* @param envelop
* @return
* @throws Exception
*/
public String decodeEnvelop(String priKey, String envelop) throws Exception;
/**
* 数字信封解密 返回明文
* @param priKey
* @param envelop
* @return
* @throws Exception
*/
public String decodeEnvelop(String priKey, byte[] envelop) throws Exception;
/**
* sm2加密
* @param publicKey
* @param data
* @return
* @throws Exception
*/
public String encrypt(String publicKey, String data) throws Exception;
/**
* sm2解密
* @param privateKey
* @param encryptedData
* @return
* @throws Exception
*/
public String decrypt(String privateKey, String encryptedData) throws Exception;
/**
* p7加签
* @param prikey 私钥
* @param cert 证书
* @param oriData 数据
* @return
* @throws Exception
*/
public String p7Sign(String prikey, String cert ,String oriData) throws Exception;
/**
* p7验签
* @param sign 签名值
* @param source 原文
* @return
* @throws Exception
*/
public Boolean p7Verify(String sign,String source) throws Exception;
/**
* p1加签
* @param prikey
* @param oriData
* @return
* @throws Exception
*/
public String p1Sign(String prikey,String oriData) throws Exception;
/**
* p1验签
* @param publicKey
* @param data
* @param signature
* @return
* @throws Exception
*/
public boolean verify(String publicKey, String data, String signature) throws Exception;
/**
* sm2加签
* @param prikey
* @param oriData
* @return
* @throws Exception
*/
public String sm2Sign(String prikey,String oriData) throws Exception;
/**
* sm2验签
* @param publicKey
* @param data
* @param signature
* @return
* @throws Exception
*/
public boolean sm2SignVerify(String publicKey, String data, String signature) throws Exception;
/**
* p1验签
* @param publicKey
* @param data
* @param charset
* @param signature
* @return
* @throws Exception
*/
public boolean verify(String publicKey, String data, String charset, String signature) throws Exception;
/**
* 获取业务站点号
* @param signature
* @return
* @throws Exception
*/
public String getCommonNameFromP7Sign(String signature) throws Exception;
/**
* sm3算法
* @param source
* @return
* @throws Exception
*/
public String sm3Hash(String source) throws Exception;
}
package cn.anicert.secure.access.crypto.service.impl;
import cn.anicert.module.sign.bc.ICryptoService;
import cn.anicert.module.sign.bc.mix.MixCryptoFactory;
import cn.anicert.module.sign.bc.std.Asymmetric;
import cn.anicert.module.sign.bc.std.Symmetric;
import cn.anicert.secure.access.crypto.util.CertUtils;
import cn.anicert.secure.access.crypto.util.SM2Utils;
import cn.anicert.secure.access.crypto.util.SM3Utils;
import cn.anicert.secure.access.crypto.service.ISoftCryptoService;
import cn.hutool.core.io.FileUtil;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
* @Description
* @Date 2024/5/29 15:35
* @Author yourongbin
**/
public class SoftCryptoServiceImpl implements ISoftCryptoService {
private ICryptoService signService = MixCryptoFactory.getInstance(Asymmetric.SM2, Symmetric.SM4);
@Override
public String encodeEnvelop(String cert, String data) throws Exception {
return encodeEnvelop(cert, data.getBytes());
}
@Override
public String encodeEnvelop(String cert, byte[] dataByte) throws Exception {
return Base64.getEncoder().encodeToString(SM2Utils.encodeEnvelop(cert, dataByte));
}
@Override
public String decodeEnvelop(String priKey, String envelop) throws Exception {
return decodeEnvelop(priKey, Base64.getDecoder().decode(envelop));
}
@Override
public String decodeEnvelop(String priKey, byte[] envelop) throws Exception {
return new String(SM2Utils.decodeEnvelop(priKey, envelop), StandardCharsets.UTF_8);
}
@Override
public String encrypt(String publicKey, String data) throws Exception {
return SM2Utils.encrypt(publicKey, data);
}
@Override
public String decrypt(String privateKey, String encryptedData) throws Exception {
return SM2Utils.decrypt(privateKey, encryptedData);
}
@Override
public String p7Sign(String prikey, String cert, String oriData) throws Exception {
return SM2Utils.pkcs7SignDetach(prikey, cert, oriData);
}
@Override
public Boolean p7Verify(String sign, String source) throws Exception {
return SM2Utils.pkcs7Verify(Base64.getDecoder().decode(sign), source.getBytes());
}
@Override
public String p1Sign(String prikey, String oriData) throws Exception {
return SM2Utils.sign(prikey, oriData);
}
@Override
public boolean verify(String publicKey, String data, String signature) throws Exception {
return verify(publicKey, data, "utf-8", signature);
}
@Override
public String sm2Sign(String prikey, String oriData) throws Exception {
return Base64.getEncoder().encodeToString(
signService.sign(prikey, oriData.getBytes(StandardCharsets.UTF_8)));
}
@Override
public boolean sm2SignVerify(String publicKey, String data, String signature) throws Exception {
byte[] byteSign = Base64.getDecoder().decode(signature);
byte[] byteBody = data.getBytes(StandardCharsets.UTF_8);
return signService.verifySign(publicKey, byteSign, byteBody);
}
@Override
public boolean verify(String publicKey, String data, String charset, String signature) throws Exception {
return SM2Utils.verify(publicKey, data, charset, signature);
}
@Override
public String getCommonNameFromP7Sign(String signature) throws Exception {
return CertUtils.getSubjectDn(signature);
}
@Override
public String sm3Hash(String source) throws Exception {
return SM3Utils.sm3Hash(source.getBytes("UTF-16LE"));
}
}
package cn.anicert.secure.access.crypto.util;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1Primitive;
import java.io.IOException;
/**
* @Description
* @Date 2024/5/30 17:18
* @Author yourongbin
**/
public class ASN1Util {
public static ASN1Object checkAndGetASN1Object(byte[] in)
throws IllegalArgumentException
{
ASN1InputStream din = null;
ASN1Primitive pkcs;
try
{
din = new ASN1InputStream(in);
pkcs = din.readObject();
} catch (IOException var11) {
throw new IllegalArgumentException("failed to construct sequence from byte[]:" + var11.getMessage());
} finally {
if (din != null) {
try {
din.close();
} catch (Exception var10) {
var10.printStackTrace();
}
}
}
return pkcs;
}
}
package cn.anicert.secure.access.crypto.util;
public class Base64 {
public Base64() {
}
public static String encode(byte[] data) {
return org.apache.commons.codec.binary.Base64.encodeBase64String(data);
}
public static byte[] decode(String data) {
return org.apache.commons.codec.binary.Base64.decodeBase64(data);
}
}
package cn.anicert.secure.access.crypto.util;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.Security;
/**
* @company cn.anicert
* @author: wangpengyuan
* @create: 2024-03-07
* @description 密码学基础类
**/
public class BaseCryptoUtil {
static {
Security.addProvider(new BouncyCastleProvider());
}
}
\ No newline at end of file
package cn.anicert.secure.access.crypto.util;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECPoint;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.bouncycastle.math.ec.ECCurve;
public class SM2KeyUtil {
private static final X9ECParameters ecParameters;
private static final ECNamedCurveSpec sm2Spec;
private static final BouncyCastleProvider bcProvider;
private static final KeyPairGenerator keyGenerator;
private static final KeyFactory keyFactory;
private static final ECCurve curve;
public SM2KeyUtil() {
}
public static KeyPair genKeyPair() {
return keyGenerator.generateKeyPair();
}
public static byte[] encodeWithPoint(BCECPublicKey pub) {
return pub.getQ().getEncoded(false);
}
public static BCECPublicKey decodeWithPoint(byte[] data) throws InvalidKeySpecException {
ECPoint point = EC5Util.convertPoint(curve.decodePoint(data));
return (BCECPublicKey)keyFactory.generatePublic(new ECPublicKeySpec(point, sm2Spec));
}
public static String encodeStrWithPoint(BCECPublicKey pub) {
return Base64.encode(encodeWithPoint(pub));
}
public static BCECPublicKey decodeWithPoint(String base64) throws InvalidKeySpecException {
return decodeWithPoint(Base64.decode(base64));
}
public static String getX64(BCECPublicKey pub) {
return Base64.encode(pub.getQ().getXCoord().getEncoded());
}
public static String getY64(BCECPublicKey pub) {
return Base64.encode(pub.getQ().getYCoord().getEncoded());
}
public static BCECPublicKey fromXY(String x64, String y64) throws InvalidKeySpecException {
return fromXY(Base64.decode(x64), Base64.decode(y64));
}
public static byte[] getX(BCECPublicKey pub) {
return pub.getQ().getXCoord().getEncoded();
}
public static byte[] getY(BCECPublicKey pub) {
return pub.getQ().getYCoord().getEncoded();
}
public static BCECPublicKey fromXY(byte[] x, byte[] y) throws InvalidKeySpecException {
ECPoint point = EC5Util.convertPoint(curve.createPoint(new BigInteger(1, x), new BigInteger(1, y)));
return (BCECPublicKey)keyFactory.generatePublic(new ECPublicKeySpec(point, sm2Spec));
}
public static byte[] encodeWithBigInteger(BCECPrivateKey pri) {
return pri.getD().toByteArray();
}
public static BCECPrivateKey decodeWithBigInteger(byte[] data) throws InvalidKeySpecException {
return (BCECPrivateKey)keyFactory.generatePrivate(new ECPrivateKeySpec(new BigInteger(1, data), sm2Spec));
}
public static String encodeStrWithBigInteger(BCECPrivateKey pri) {
return Base64.encode(encodeWithBigInteger(pri));
}
public static BCECPrivateKey decodeWithBigInteger(String base64) throws InvalidKeySpecException {
return decodeWithBigInteger(Base64.decode(base64));
}
public static byte[] encodeWithKey(Key key) {
return key.getEncoded();
}
public static BCECPublicKey decodeWithX509(byte[] data) throws InvalidKeySpecException {
return (BCECPublicKey)keyFactory.generatePublic(new X509EncodedKeySpec(data));
}
public static BCECPrivateKey decodeWithPKCS8(byte[] data) throws InvalidKeySpecException {
return (BCECPrivateKey)keyFactory.generatePrivate(new PKCS8EncodedKeySpec(data));
}
public static String encodeStrWithKey(Key key) {
return Base64.encode(encodeWithKey(key));
}
public static BCECPublicKey decodeWithX509(String base64) throws InvalidKeySpecException {
return decodeWithX509(Base64.decode(base64.replaceAll("-----.*-----", "")));
}
public static BCECPrivateKey decodeWithPKCS8(String base64) throws InvalidKeySpecException {
return decodeWithPKCS8(Base64.decode(base64.replaceAll("-----.*-----", "")));
}
public static CipherParameters forParameters(Key key) throws InvalidKeyException {
if (key instanceof PublicKey) {
return ECUtil.generatePublicKeyParameter((PublicKey)key);
} else if (key instanceof PrivateKey) {
return ECUtil.generatePrivateKeyParameter((PrivateKey)key);
} else {
throw new InvalidKeyException("not expect key type");
}
}
static {
try {
ecParameters = GMNamedCurves.getByName("sm2p256v1");
sm2Spec = new ECNamedCurveSpec(GMObjectIdentifiers.sm2p256v1.toString(), ecParameters.getCurve(), ecParameters.getG(), ecParameters.getN());
bcProvider = new BouncyCastleProvider();
keyGenerator = KeyPairGenerator.getInstance("EC", bcProvider);
keyGenerator.initialize(sm2Spec, new SecureRandom());
keyFactory = KeyFactory.getInstance("EC", bcProvider);
curve = ecParameters.getCurve();
} catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException var1) {
throw new RuntimeException("无法提供SM2算法", var1);
}
}
public static void main(String[] args) {
KeyPair smKeyPair = SM2KeyUtil.genKeyPair();
String publicKey = Base64.encode(smKeyPair.getPublic().getEncoded());
String privateKey = Base64.encode(smKeyPair.getPrivate().getEncoded());
System.out.println("public key:"+ publicKey);
System.out.println("private key:"+ privateKey);
}
}
package cn.anicert.secure.access.crypto.util;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Hex;
import java.util.Arrays;
public class SM3Utils extends BaseCryptoUtil {
/**
* 计算SM3摘要值
*
* @param srcData 原文
* @return 摘要值,对于SM3算法来说是32字节
*/
public static String sm3Hash(byte[] srcData) {
SM3Digest digest = new SM3Digest();
digest.update(srcData, 0, srcData.length);
byte[] hash = new byte[digest.getDigestSize()];
digest.doFinal(hash, 0);
String result = Hex.toHexString(hash);
return result.toUpperCase();
}
/**
* 计算两项身份信息hash
*
* @param idno 身份证号
* @param name 姓名
* @return 摘要值,对于SM3算法来说是32字节
*/
public static String sm3Hash(String idno, String name) {
String str = idno + name;
byte[] srcData = str.getBytes();
return sm3Hash(srcData);
}
}
package cn.anicert.secure.access.crypto.util;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.engines.SM4Engine;
import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
import org.bouncycastle.crypto.macs.GMac;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.paddings.BlockCipherPadding;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
public class SM4Utils extends BaseCryptoUtil {
static {
Security.addProvider(new BouncyCastleProvider());
}
public static final String ALGORITHM_NAME = "SM4";
public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
public static final String ALGORITHM_NAME_ECB_NOPADDING = "SM4/ECB/NoPadding";
public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding";
public static final String ALGORITHM_NAME_CBC_NOPADDING = "SM4/CBC/NoPadding";
/**
* SM4算法目前只支持128位(即密钥16字节)
*/
public static final int DEFAULT_KEY_SIZE = 16;
public static byte[] generateKey() throws NoSuchAlgorithmException, NoSuchProviderException {
return generateKey(DEFAULT_KEY_SIZE);
}
public static byte[] generateKey(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException {
// KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
// kg.init(keySize, new SecureRandom());
SecureRandom secureRandom = new SecureRandom();
byte[] random = new byte[keySize];
secureRandom.nextBytes(random);
return random;
// return kg.generateKey().getEncoded();
}
public static byte[] encrypt_ECB_Padding(byte[] key, byte[] data)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
}
public static byte[] decrypt_ECB_Padding(byte[] key, byte[] cipherText)
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
return cipher.doFinal(cipherText);
}
public static byte[] encrypt_ECB_NoPadding(byte[] key, byte[] data)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
}
public static byte[] decrypt_ECB_NoPadding(byte[] key, byte[] cipherText)
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
Cipher cipher = generateECBCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.DECRYPT_MODE, key);
return cipher.doFinal(cipherText);
}
public static byte[] encrypt_CBC_Padding(byte[] key, byte[] iv, byte[] data)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,
InvalidAlgorithmParameterException {
Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key, iv);
return cipher.doFinal(data);
}
public static byte[] decrypt_CBC_Padding(byte[] key, byte[] iv, byte[] cipherText)
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
InvalidAlgorithmParameterException {
Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key, iv);
return cipher.doFinal(cipherText);
}
public static byte[] encrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] data)
throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,
InvalidAlgorithmParameterException {
Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.ENCRYPT_MODE, key, iv);
return cipher.doFinal(data);
}
public static byte[] decrypt_CBC_NoPadding(byte[] key, byte[] iv, byte[] cipherText)
throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
InvalidAlgorithmParameterException {
Cipher cipher = generateCBCCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.DECRYPT_MODE, key, iv);
return cipher.doFinal(cipherText);
}
public static byte[] doCMac(byte[] key, byte[] data) throws NoSuchProviderException, NoSuchAlgorithmException,
InvalidKeyException {
Key keyObj = new SecretKeySpec(key, ALGORITHM_NAME);
return doMac("SM4-CMAC", keyObj, data);
}
public static byte[] doGMac(byte[] key, byte[] iv, int tagLength, byte[] data) {
org.bouncycastle.crypto.Mac mac = new GMac(new GCMBlockCipher(new SM4Engine()), tagLength * 8);
return doMac(mac, key, iv, data);
}
/**
* 默认使用PKCS7Padding/PKCS5Padding填充的CBCMAC
*
* @param key
* @param iv
* @param data
* @return
*/
public static byte[] doCBCMac(byte[] key, byte[] iv, byte[] data) {
SM4Engine engine = new SM4Engine();
org.bouncycastle.crypto.Mac mac = new CBCBlockCipherMac(engine, engine.getBlockSize() * 8, new PKCS7Padding());
return doMac(mac, key, iv, data);
}
/**
* @param key
* @param iv
* @param padding 可以传null,传null表示NoPadding,由调用方保证数据必须是BlockSize的整数倍
* @param data
* @return
* @throws Exception
*/
public static byte[] doCBCMac(byte[] key, byte[] iv, BlockCipherPadding padding, byte[] data) throws Exception {
SM4Engine engine = new SM4Engine();
if (padding == null) {
if (data.length % engine.getBlockSize() != 0) {
throw new Exception("if no padding, data length must be multiple of SM4 BlockSize");
}
}
org.bouncycastle.crypto.Mac mac = new CBCBlockCipherMac(engine, engine.getBlockSize() * 8, padding);
return doMac(mac, key, iv, data);
}
private static byte[] doMac(org.bouncycastle.crypto.Mac mac, byte[] key, byte[] iv, byte[] data) {
CipherParameters cipherParameters = new KeyParameter(key);
mac.init(new ParametersWithIV(cipherParameters, iv));
mac.update(data, 0, data.length);
byte[] result = new byte[mac.getMacSize()];
mac.doFinal(result, 0);
return result;
}
private static byte[] doMac(String algorithmName, Key key, byte[] data) throws NoSuchProviderException,
NoSuchAlgorithmException, InvalidKeyException {
Mac mac = Mac.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
mac.init(key);
mac.update(data);
return mac.doFinal();
}
private static Cipher generateECBCipher(String algorithmName, int mode, byte[] key)
throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
InvalidKeyException {
Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
cipher.init(mode, sm4Key);
return cipher;
}
private static Cipher generateCBCCipher(String algorithmName, int mode, byte[] key, byte[] iv)
throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
NoSuchProviderException, NoSuchPaddingException {
Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(mode, sm4Key, ivParameterSpec);
return cipher;
}
}
package cn.anicert.secure.access.enums;
public enum Configs {
customerNo("customerNo", "业务站点号", true),
accessUrl("accessUrl", "安全接入平台地址", true),
signPrivateKey("signPrivateKey", "签名私钥", true),
signVerifyPublicKey("signVerifyPublicKey", "验签公钥", true),
dataEncryptKey("dataEncryptKey", "数据加密秘钥", false);
private String key;
private String name;
private boolean necessary;
Configs(String key, String name) {
this(key, name, false);
}
Configs(String key, String name, boolean necessary) {
this.key = key;
this.name = name;
this.necessary = necessary;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isNecessary() {
return necessary;
}
public void setNecessary(boolean necessary) {
this.necessary = necessary;
}
}
package cn.anicert.secure.access.exceptions;
public class SecureAccessConfigException extends RuntimeException {
public SecureAccessConfigException() {
super();
}
public SecureAccessConfigException(String message) {
super(message);
}
}
package cn.anicert.secure.access.utils;
import cn.anicert.secure.access.config.ConfigReader;
import cn.anicert.secure.access.config.bean.AccessConfig;
import cn.anicert.secure.access.enums.Configs;
public class ConfigUtil {
private static AccessConfig accessConfig;
public static synchronized void initConfig(String... filePath) {
accessConfig = ConfigReader.readConfig(filePath);
}
public static synchronized void initConfig(AccessConfig config) {
accessConfig = config;
}
public static AccessConfig getConfig() {
return accessConfig;
}
public static void updateConfigDataEncryptKey(String newKey) {
accessConfig=ConfigReader.writeConfig(Configs.dataEncryptKey.getKey(), newKey);
}
}
package cn.anicert.secure.access.utils;
import cn.hutool.log.StaticLog;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.TrustAllStrategy;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.config.Registry;
import org.apache.hc.core5.http.config.RegistryBuilder;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.ssl.SSLContextBuilder;
import org.apache.hc.core5.util.Timeout;
import javax.net.ssl.SSLContext;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
public class HttpUtil {
private final PoolingHttpClientConnectionManager connPoolMng;
private final RequestConfig requestConfig;
private final SSLContext sslContext;
private volatile static HttpUtil httpUtilInstance;
/**
* 私有构造方法
* 单例中连接池初始化一次
*/
private HttpUtil() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
//初始化http连接池
sslContext = SSLContextBuilder.create()
.loadTrustMaterial(new TrustAllStrategy())
.build();
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https",new SSLConnectionSocketFactory(sslContext))
.build();
connPoolMng = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
connPoolMng.setMaxTotal(ConfigUtil.getConfig().getPoolMaxConn());
connPoolMng.setDefaultMaxPerRoute(ConfigUtil.getConfig().getPoolMaxConn());
//初始化请求超时控制参数
requestConfig = RequestConfig.custom()
//从线程池中获取线程超时时间
.setConnectionRequestTimeout(Timeout.ofMilliseconds(ConfigUtil.getConfig().getConnectionRequestTimeout()))
//设置数据读取超时时间
.setResponseTimeout(Timeout.ofMilliseconds(ConfigUtil.getConfig().getResponseTimeout()))
.build();
}
/**
* 单例模式
* 使用双检锁机制,线程安全且在多线程情况下能保持高性能
*
* @return HttpUtil
*/
public static HttpUtil getInstance() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
if (httpUtilInstance == null) {
synchronized (HttpUtil.class) {
if (httpUtilInstance == null) {
httpUtilInstance = new HttpUtil();
}
}
}
return httpUtilInstance;
}
/**
* 获取client客户端
*
* @return
*/
public CloseableHttpClient getClient() {
return HttpClients.custom()
.setConnectionManager(connPoolMng)
.setDefaultRequestConfig(requestConfig)
.build();
}
/**
* post请求
*
* @param postUrl
* @param jsonStr
* @return String
* @throws Exception
*/
public String postRequest(String postUrl, String jsonStr) throws Exception {
String result;
HttpPost post = new HttpPost(postUrl);
StringEntity reqEntity = new StringEntity(jsonStr, ContentType.APPLICATION_JSON);
post.setEntity(reqEntity);
result = getClient().execute(post, response -> {
StaticLog.debug("[安全接入平台]--请求地址--{}--响应状态--{}", postUrl, response.getCode());
return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
});
return result;
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package cn.anicert.secure.access.utils;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
public class JSONUtil {
private static final ObjectMapper mapper = new ObjectMapper();
public static final JavaType[] EMPTY_TYPES = new JavaType[0];
public static final String YYYYMMDDHHMMSS = "yyyy-MM-dd HH:mm:ss";
public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public JSONUtil() {
}
public static <T> T json2Object(String jsonStr, Class<T> clazz) {
try {
return mapper.readValue(jsonStr, clazz);
} catch (Exception var3) {
throw new RuntimeException(var3);
}
}
public static String object2Json(Object obj) {
try {
return mapper.writeValueAsString(obj);
} catch (Exception var2) {
throw new RuntimeException(var2);
}
}
public static String toJson(Object obj) {
try {
return mapper.writeValueAsString(obj);
} catch (Exception var2) {
throw new RuntimeException(var2);
}
}
public static String toPrettyJason(Object obj) {
try {
return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
} catch (Exception var2) {
throw new RuntimeException(var2);
}
}
public static String object2DateFormatJson(Object obj) {
return object2DateFormatJson(obj, "yyyy-MM-dd HH:mm:ss");
}
public static String object2DateFormatJson(Object obj, String dateFmt) {
try {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(Feature.ALLOW_COMMENTS, false);
mapper.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
mapper.configure(Feature.ALLOW_SINGLE_QUOTES, true);
mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
mapper.setDateFormat(new SimpleDateFormat(dateFmt));
mapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
mapper.registerModule(new JavaTimeModule());
return mapper.writeValueAsString(obj);
} catch (Exception var3) {
throw new RuntimeException(var3);
}
}
public static <T> List<T> json2ObjectList(String jsonString, Class<T> clazz) {
try {
if (jsonString == null) {
return null;
} else {
List<T> list = (List)mapper.readValue(jsonString, new TypeReference<List<T>>() {
});
return list;
}
} catch (Exception var3) {
throw new RuntimeException(var3);
}
}
public static List<String> json2StringList(String jsonString) {
try {
if (!"".equals(jsonString) && null != jsonString) {
JsonNode jsonNode = mapper.readTree(jsonString);
List<String> list = new ArrayList();
jsonNode.forEach((node) -> {
list.add(node.toString());
});
return list;
} else {
return null;
}
} catch (Exception var3) {
throw new RuntimeException(var3);
}
}
public static Map<String, Object> json2Map(String jsonString) {
try {
return (Map)mapper.readValue(jsonString, new TypeReference<Map<String, Object>>() {
});
} catch (Exception var2) {
throw new RuntimeException(var2);
}
}
public static List<Map<String, Object>> json2MapList(String jsonString) {
try {
return (List)mapper.readValue(jsonString, new TypeReference<List<Map<String, Object>>>() {
});
} catch (Exception var2) {
throw new RuntimeException(var2);
}
}
public static Object[] json2Array(String jsonString) {
try {
List<Object> objs = (List)mapper.readValue(jsonString, new TypeReference<List>() {
});
return objs.toArray();
} catch (Exception var2) {
throw new RuntimeException(var2);
}
}
public static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses);
}
public static JsonNode json2JSONArray(String jsonString) {
try {
JsonNode jsonNode = mapper.readTree(jsonString);
JsonNodeType nodeType = jsonNode.getNodeType();
if (nodeType.equals(JsonNodeType.ARRAY)) {
return jsonNode;
} else {
throw new IllegalArgumentException("json字符串不是数组");
}
} catch (Exception var3) {
throw new RuntimeException(var3);
}
}
public static String array2Json(String[] strs) {
try {
ArrayNode arrayNode = new ArrayNode(new JsonNodeFactory(false));
String[] var2 = strs;
int var3 = strs.length;
for(int var4 = 0; var4 < var3; ++var4) {
String str = var2[var4];
JsonNode jsonNode = mapper.readTree(str);
arrayNode.add(jsonNode);
}
return arrayNode.toString();
} catch (Exception var7) {
throw new RuntimeException(var7);
}
}
public static <T> T[] jsonArrayToObjectArray(String jsonStr, Class<T[]> t) {
try {
T[] ts = (T[]) mapper.readValue(jsonStr, t);
return ts;
} catch (Exception var3) {
throw new RuntimeException(var3);
}
}
static {
mapper.setSerializationInclusion(Include.NON_NULL);
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(Feature.ALLOW_COMMENTS, false);
mapper.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
mapper.configure(Feature.ALLOW_SINGLE_QUOTES, true);
mapper.configure(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS, true);
mapper.setDateFormat(sdf);
mapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
mapper.registerModule(new JavaTimeModule());
}
}
package cn.anicert.secure.access.utils;
import cn.anicert.module.sign.bc.ICryptoService;
import cn.anicert.module.sign.bc.exception.BadKeyException;
import cn.anicert.module.sign.bc.exception.BadSignatureException;
import cn.anicert.module.sign.bc.mix.MixCryptoFactory;
import cn.anicert.module.sign.bc.std.Asymmetric;
import cn.anicert.module.sign.bc.std.Symmetric;
import org.apache.commons.codec.binary.Base64;
import java.nio.charset.StandardCharsets;
public class SignUtil {
private final static ICryptoService cryptoService = MixCryptoFactory.getInstance(Asymmetric.SM2, Symmetric.SM4);
public static String sign(String signBody) throws BadSignatureException, BadKeyException {
return sign(signBody, ConfigUtil.getConfig().getSignPrivateKey());
}
public static String sign(String signBody, String signPriKey) throws BadSignatureException, BadKeyException {
return Base64.encodeBase64String(cryptoService.sign(signPriKey, signBody.getBytes(StandardCharsets.UTF_8)));
}
public static boolean verifySign(String body, String sign) throws BadSignatureException, BadKeyException {
return verifySign(body, sign, ConfigUtil.getConfig().getSignVerifyPublicKey());
}
public static boolean verifySign(String body, String sign, String key) throws BadSignatureException, BadKeyException {
byte[] byteSign = Base64.decodeBase64(sign);
byte[] byteBody = body.getBytes(StandardCharsets.UTF_8);
return cryptoService.verifySign(key, byteSign, byteBody);
}
}
package com.liquidnet.common.third.secureaccess.config;
import cn.anicert.secure.access.config.bean.AccessConfig;
import cn.anicert.secure.access.crypto.service.ISoftCryptoService;
import cn.anicert.secure.access.crypto.service.impl.SoftCryptoServiceImpl;
import cn.anicert.secure.access.utils.ConfigUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
@Slf4j
@Configuration
public class SecureAccessConfiguration {
private final SecureAccessProperties properties;
public SecureAccessConfiguration(SecureAccessProperties properties) {
this.properties = properties;
}
@Bean
public ISoftCryptoService softCryptoService() {
return new SoftCryptoServiceImpl();
}
@PostConstruct
public void initSecureAccessSdk() {
if (StringUtils.isAnyBlank(
properties.getCustomerNo(),
properties.getAccessUrl(),
properties.getSignPrivateKey(),
properties.getSignVerifyPublicKey())) {
log.warn("[secure-access] 配置不完整,网证认证功能不可用");
return;
}
AccessConfig accessConfig = new AccessConfig();
accessConfig.setOrgId(properties.getOrgId());
accessConfig.setCustomerNo(properties.getCustomerNo());
accessConfig.setAccessUrl(properties.getAccessUrl());
accessConfig.setSignPrivateKey(properties.getSignPrivateKey());
accessConfig.setSignVerifyPublicKey(properties.getSignVerifyPublicKey());
accessConfig.setDataEncryptKey(properties.getDataEncryptKey());
ConfigUtil.initConfig(accessConfig);
log.info("[secure-access] SDK 初始化完成, customerNo={}", properties.getCustomerNo());
}
}
package com.liquidnet.common.third.secureaccess.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "liquidnet.secure-access")
public class SecureAccessProperties {
/** 机构 ID */
private String orgId;
/** 业务站点号 */
private String customerNo;
/** 安全接入平台根地址,如 https://sap.ctdid.cn:29002 */
private String accessUrl;
/** 机构侧 P1 签名私钥(Base64) */
private String signPrivateKey;
/** 安全接入平台验签公钥(Base64) */
private String signVerifyPublicKey;
/** 数据加密证书(Base64) */
private String dataEncryptKey;
/** 应用名称,对应接口文档 appName */
private String appName;
/** 认证模式:R01 网络身份认证凭证 */
private String mode = "R01";
/** 公共服务平台 auth/request 地址(测试/生产由配置切换) */
private String forwardUrl = "http://authtest.ctdid.cn:10002/uentrance/interf/auth/request";
}
package com.liquidnet.common.third.secureaccess.model;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class NiaAuthRequest {
/** 与前端 SDK 一致的 32 位业务序列号 */
private final String bizSeq;
/** 前端拉起网证 App 后 getAuthResult 获取 */
private final String idCardAuthData;
}
package com.liquidnet.common.third.secureaccess.model;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class NiaAuthResult {
private final boolean success;
private final String resultCode;
private final String resultDesc;
private final String bizSeq;
private final String bizSerialNo;
/** 网络身份应用标识标准 Base64(40 字节 → 56 字符) */
private final String openId;
private final String photoCompareScore;
private final String encryptedIdInfo;
/** 失败阶段:CONFIG / PARAM / SAP / PARSE / PLATFORM / UNKNOWN */
private final String stage;
/** SAP/平台原始响应摘要,便于联调 */
private final String rawResponse;
@Override
public String toString() {
return "NiaAuthResult{success=" + success
+ ", resultCode=" + resultCode
+ ", resultDesc=" + resultDesc
+ ", bizSeq=" + bizSeq
+ ", bizSerialNo=" + bizSerialNo
+ ", openId=" + openId
+ ", stage=" + stage
+ '}';
}
}
package com.liquidnet.common.third.secureaccess.model;
import lombok.Builder;
import lombok.Getter;
import org.apache.commons.codec.binary.Hex;
/**
* PID(认证服务凭据)按表 6 解码后的结构。
*/
@Getter
@Builder
public class NiaPidParsed {
private static final int TIMESTAMP_LEN = 14;
private final int totalLen;
private final int version;
/** 第 1–40 字节:网络身份应用标识(二进制,非文本) */
private final byte[] identifier;
/** 第 41–54 字节:yyyymmddhhmmss */
private final String timestamp;
/** 第 55+ 字节:签名 */
private final byte[] signature;
public String identifierHex() {
return identifier == null ? null : Hex.encodeHexString(identifier).toUpperCase();
}
/** 标识段可视化:可打印字符原样,不可打印用 '.' */
public String identifierVisual() {
if (identifier == null) {
return null;
}
StringBuilder sb = new StringBuilder(identifier.length);
for (byte b : identifier) {
sb.append(b >= 32 && b < 127 ? (char) b : '.');
}
return sb.toString();
}
public String signatureHexPrefix(int maxBytes) {
if (signature == null || signature.length == 0) {
return null;
}
int len = Math.min(signature.length, maxBytes);
byte[] head = new byte[len];
System.arraycopy(signature, 0, head, 0, len);
String hex = Hex.encodeHexString(head);
return signature.length > maxBytes ? hex + "..." : hex;
}
}
package com.liquidnet.common.third.secureaccess.util;
import com.liquidnet.common.third.secureaccess.model.NiaPidParsed;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import java.nio.charset.StandardCharsets;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;
/**
* PID 解析与 openId 编码,与网证官方说明一致:
* <pre>
* byte[] raw = Base64.getDecoder().decode(pid);
* System.arraycopy(raw, 1, identifier, 0, 40);
* </pre>
* 存库 openId 取 40 字节标识的<strong>标准 Base64</strong>(56 字符,可放进 varchar(64))。
*/
public final class NiaPidUtils {
public static final int IDENTIFIER_BYTE_LEN = 40;
/** 标准 Base64(含填充)固定 56 字符 */
public static final int OPEN_ID_LEN = 56;
private static final Decoder JDK_BASE64_DECODER = java.util.Base64.getDecoder();
private static final Encoder JDK_BASE64_ENCODER = java.util.Base64.getEncoder();
private static final int TIMESTAMP_LEN = 14;
private static final int MIN_PID_LEN = 1 + IDENTIFIER_BYTE_LEN + TIMESTAMP_LEN;
private NiaPidUtils() {
}
public static NiaPidParsed parse(String pidBase64) {
if (StringUtils.isBlank(pidBase64)) {
return null;
}
byte[] raw;
try {
raw = JDK_BASE64_DECODER.decode(pidBase64.trim());
} catch (IllegalArgumentException ex) {
return null;
}
if (raw.length < MIN_PID_LEN) {
return null;
}
int version = raw[0] & 0xFF;
byte[] identifier = new byte[IDENTIFIER_BYTE_LEN];
System.arraycopy(raw, 1, identifier, 0, IDENTIFIER_BYTE_LEN);
byte[] tsBytes = new byte[TIMESTAMP_LEN];
System.arraycopy(raw, 1 + IDENTIFIER_BYTE_LEN, tsBytes, 0, TIMESTAMP_LEN);
String timestamp = new String(tsBytes, StandardCharsets.US_ASCII);
if (!timestamp.matches("\\d{14}")) {
return null;
}
byte[] signature = new byte[raw.length - MIN_PID_LEN];
if (signature.length > 0) {
System.arraycopy(raw, MIN_PID_LEN, signature, 0, signature.length);
}
return NiaPidParsed.builder()
.totalLen(raw.length)
.version(version)
.identifier(identifier)
.timestamp(timestamp)
.signature(signature)
.build();
}
/** 40 字节标识 → 标准 Base64(56 字符) */
public static String extractOpenId(String pid) {
NiaPidParsed parsed = parse(pid);
if (parsed == null || parsed.getIdentifier() == null) {
return null;
}
return encodeOpenId(parsed.getIdentifier());
}
public static String encodeOpenId(byte[] identifier) {
if (identifier == null || identifier.length != IDENTIFIER_BYTE_LEN) {
return null;
}
return JDK_BASE64_ENCODER.encodeToString(identifier);
}
/** 日志排查用:40 字节标识 → 大写 hex */
public static String toHex(byte[] identifier) {
if (identifier == null || identifier.length != IDENTIFIER_BYTE_LEN) {
return null;
}
return Hex.encodeHexString(identifier).toUpperCase();
}
public static byte[] toIdentifierBytes(String openId) {
if (StringUtils.isBlank(openId)) {
return null;
}
try {
byte[] decoded = JDK_BASE64_DECODER.decode(openId.trim());
return decoded.length == IDENTIFIER_BYTE_LEN ? decoded : null;
} catch (IllegalArgumentException ex) {
return null;
}
}
public static boolean isValidOpenId(String openId) {
if (StringUtils.isBlank(openId) || openId.length() != OPEN_ID_LEN) {
return false;
}
return toIdentifierBytes(openId) != null;
}
public static String maskPid(String pid) {
if (StringUtils.isBlank(pid)) {
return null;
}
if (pid.length() <= 16) {
return "***";
}
return pid.substring(0, 8) + "..." + pid.substring(pid.length() - 8);
}
}
package com.liquidnet.common.third.secureaccess.util;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class NiaPidUtilsTest {
private static final String PID_1 =
"Al7Y07Qo4P5eHosb4ADGQC4ou83HI+U6tgsUXC3uQYlZMDAwMDA3MTIyMDI2MDYyMzE5MTg1MjBGAiEAt5CKWHwrjso5x8i4Vl5cNiSCZAFwQefMXHyPoNIV4IMCIQCV1/pVvwyOlF6I6qLywzXHCGDmT6OVnBPX3+ZyMBWYjg==";
private static final String PID_2 =
"Al7Y07Qo4P5eHosb4ADGQC4ou83HI+U6tgsUXC3uQYlZMDAwMDA3MTIyMDI2MDYyNDExNTMxNDBFAiBfpnywTM/7efM4tAK5GglCWG39yMmFdYpfZDFRIpg30gIhAKkCuggZx2KoW0Uf1xy2BmF7OdBlCEqjlzujwUBq1ZDU";
private static final String OPEN_ID_B64 =
"XtjTtCjg/l4eixvgAMZALii7zccj5Tq2CxRcLe5BiVkwMDAwMDcxMg==";
@Test
void openId_shouldBeStandardBase64AndStableAcrossAuths() {
String openId1 = NiaPidUtils.extractOpenId(PID_1);
String openId2 = NiaPidUtils.extractOpenId(PID_2);
assertEquals(openId1, openId2);
assertEquals(56, openId1.length());
assertEquals(OPEN_ID_B64, openId1);
}
@Test
void openId_roundTrip() {
String openId = NiaPidUtils.extractOpenId(PID_1);
byte[] bytes = NiaPidUtils.toIdentifierBytes(openId);
assertNotNull(bytes);
assertEquals(openId, NiaPidUtils.encodeOpenId(bytes));
}
}
...@@ -17,5 +17,6 @@ ...@@ -17,5 +17,6 @@
<module>liquidnet-common-third-antchain</module> <module>liquidnet-common-third-antchain</module>
<module>liquidnet-common-third-xuper</module> <module>liquidnet-common-third-xuper</module>
<module>liquidnet-common-third-sqb</module> <module>liquidnet-common-third-sqb</module>
<module>liquidnet-common-third-secure-access</module>
</modules> </modules>
</project> </project>
...@@ -272,4 +272,15 @@ liquidnet: ...@@ -272,4 +272,15 @@ liquidnet:
public-key: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuf1oOZm3u5NraTs4F8AABXbtU2jSiWYp+IWmQ36vokuq6s2s3eKQR6l4RkrPPxjC86bIvjT4pApJZJrFMA4YcjY4G49wFZySfom4IPaZlKsOrNGJH0Kag0BSO9U5el1z7dMz7oP9cChbdl4mjKuqYtgnNtaPT+SqhXRQdFcc9kiVybAGs8WEGqsdwxsmD9aZTd4rQMvLEGWIj/MLdo7w1avc0WVSPQSM5jRHjjQmUzEuusv+QGcDt3ttNaip2uo1xoQdcwILYmS6fnWL8xKw4V8lX0CWypUKIZcIc1Y/1N8VeUN+8MirdrS5JSghq62Yifu9A3W/mANB+S6yYwD+WQIDAQAB' public-key: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuf1oOZm3u5NraTs4F8AABXbtU2jSiWYp+IWmQ36vokuq6s2s3eKQR6l4RkrPPxjC86bIvjT4pApJZJrFMA4YcjY4G49wFZySfom4IPaZlKsOrNGJH0Kag0BSO9U5el1z7dMz7oP9cChbdl4mjKuqYtgnNtaPT+SqhXRQdFcc9kiVybAGs8WEGqsdwxsmD9aZTd4rQMvLEGWIj/MLdo7w1avc0WVSPQSM5jRHjjQmUzEuusv+QGcDt3ttNaip2uo1xoQdcwILYmS6fnWL8xKw4V8lX0CWypUKIZcIc1Y/1N8VeUN+8MirdrS5JSghq62Yifu9A3W/mANB+S6yYwD+WQIDAQAB'
merchant-id: 'b2d63146-934f-401f-a864-14926d952c16' merchant-id: 'b2d63146-934f-401f-a864-14926d952c16'
merchant-user-id: '6d0d632e-50e9-464e-bbb3-be76047ec835' merchant-user-id: '6d0d632e-50e9-464e-bbb3-be76047ec835'
role: 'super_admin' role: 'super_admin'
\ No newline at end of file
secure-access:
org-id: '00000712'
customer-no: '00000712AA'
access-url: 'https://sap.ctdid.cn:29002'
sign-private-key: 'MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgY1WpjKNKWWDbqDRaBTzjCwbQxUo/ZIWWES0Xrp9YzY6hRANCAATfC188MtTPD/ziXvqI2VHLOspvZ9bBh2n7LNWgaP2QIXBJ/0vIRoSMT7ZwklfgZu58gI5ig8N2XvGHxWBdyLfF' # 机构 P1 签名私钥,联调时填入
sign-verify-public-key: 'MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEUNi/1x2Bh3mGHdpTjs+jIdv62G7oAU/dKzczYtuz1yol0ivUwt3xpA7nPEacWK+IIj2ZfOev4G5qrndMEs7t6Q==' # SAP 验签公钥
data-encrypt-key: 'MIICcjCCAhagAwIBAgISEAIFAyYEAhMYU+LoH1l4EIShMAwGCCqBHM9VAYN1BQAwRDELMAkGA1UEBhMCQ04xETAPBgNVBAoMCENITkNURElEMREwDwYDVQQLDAhDSE5DVERJRDEPMA0GA1UEAwwGWVdDQTAxMB4XDTI2MDQwMjA1MjUxM1oXDTQyMDMyOTA1MjUxM1owMTEPMA0GA1UEAwwGUlpGVzAzMREwDwYDVQQLDAhDSE5DVERJRDELMAkGA1UEBhMCQ04wWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAAQunUvCkEN4ISqw4sp0+Tvr14uKQt0VpKkgpSwi0HhGOJlUrC9bmitCk0xa0+5zd6lw1Cx5o4M4xCmZQQfzxb9uo4H4MIH1MAkGA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgM4MIGXBgNVHR8EgY8wgYwwTKBKoEikRjBEMQ8wDQYDVQQDDAZZV0NBMDExETAPBgNVBAsMCENITkNURElEMREwDwYDVQQKDAhDSE5DVERJRDELMAkGA1UEBhMCQ04wPKA6oDiGNmh0dHA6Ly8xNzIuMjguMzAuODoxODA2MC9jYXdlYi9jcmwvWVdDQTAxL1lXQ0EwMV8wLmNybDAdBgNVHQ4EFgQU264a6MnK3/Ks3dVrI0BoEg2CLokwHwYDVR0jBBgwFoAUm1gG9DaAOEzQny6vTi6LlnTNgagwDAYIKoEcz1UBg3UFAANIADBFAiAPqpe2P76Eu0TBHKPqtflsCXTMUolq+FBXKK8nPhW9SwIhAKHGhPvqIAWcDHpu8xlHYJ9bkAwhJDDHNwspU7x6V0au' # 数据加密证书
app-name: '正在现场'
mode: 'R01'
forward-url: 'http://authtest.ctdid.cn:10002/uentrance/interf/auth/request'
\ No newline at end of file
...@@ -270,4 +270,16 @@ liquidnet: ...@@ -270,4 +270,16 @@ liquidnet:
public-key: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuf1oOZm3u5NraTs4F8AABXbtU2jSiWYp+IWmQ36vokuq6s2s3eKQR6l4RkrPPxjC86bIvjT4pApJZJrFMA4YcjY4G49wFZySfom4IPaZlKsOrNGJH0Kag0BSO9U5el1z7dMz7oP9cChbdl4mjKuqYtgnNtaPT+SqhXRQdFcc9kiVybAGs8WEGqsdwxsmD9aZTd4rQMvLEGWIj/MLdo7w1avc0WVSPQSM5jRHjjQmUzEuusv+QGcDt3ttNaip2uo1xoQdcwILYmS6fnWL8xKw4V8lX0CWypUKIZcIc1Y/1N8VeUN+8MirdrS5JSghq62Yifu9A3W/mANB+S6yYwD+WQIDAQAB' public-key: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuf1oOZm3u5NraTs4F8AABXbtU2jSiWYp+IWmQ36vokuq6s2s3eKQR6l4RkrPPxjC86bIvjT4pApJZJrFMA4YcjY4G49wFZySfom4IPaZlKsOrNGJH0Kag0BSO9U5el1z7dMz7oP9cChbdl4mjKuqYtgnNtaPT+SqhXRQdFcc9kiVybAGs8WEGqsdwxsmD9aZTd4rQMvLEGWIj/MLdo7w1avc0WVSPQSM5jRHjjQmUzEuusv+QGcDt3ttNaip2uo1xoQdcwILYmS6fnWL8xKw4V8lX0CWypUKIZcIc1Y/1N8VeUN+8MirdrS5JSghq62Yifu9A3W/mANB+S6yYwD+WQIDAQAB'
merchant-id: 'b2d63146-934f-401f-a864-14926d952c16' merchant-id: 'b2d63146-934f-401f-a864-14926d952c16'
merchant-user-id: '6d0d632e-50e9-464e-bbb3-be76047ec835' merchant-user-id: '6d0d632e-50e9-464e-bbb3-be76047ec835'
role: 'super_admin' role: 'super_admin'
\ No newline at end of file
secure-access:
org-id: '00000712'
customer-no: '00000712AA'
access-url: 'https://sap.ctdid.cn:29002'
sign-private-key: 'MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgY1WpjKNKWWDbqDRaBTzjCwbQxUo/ZIWWES0Xrp9YzY6hRANCAATfC188MtTPD/ziXvqI2VHLOspvZ9bBh2n7LNWgaP2QIXBJ/0vIRoSMT7ZwklfgZu58gI5ig8N2XvGHxWBdyLfF' # 机构 P1 签名私钥,联调时填入
sign-verify-public-key: 'MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEUNi/1x2Bh3mGHdpTjs+jIdv62G7oAU/dKzczYtuz1yol0ivUwt3xpA7nPEacWK+IIj2ZfOev4G5qrndMEs7t6Q==' # SAP 验签公钥
data-encrypt-key: 'MIICcjCCAhagAwIBAgISEAIFAyYEAhMYU+LoH1l4EIShMAwGCCqBHM9VAYN1BQAwRDELMAkGA1UEBhMCQ04xETAPBgNVBAoMCENITkNURElEMREwDwYDVQQLDAhDSE5DVERJRDEPMA0GA1UEAwwGWVdDQTAxMB4XDTI2MDQwMjA1MjUxM1oXDTQyMDMyOTA1MjUxM1owMTEPMA0GA1UEAwwGUlpGVzAzMREwDwYDVQQLDAhDSE5DVERJRDELMAkGA1UEBhMCQ04wWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAAQunUvCkEN4ISqw4sp0+Tvr14uKQt0VpKkgpSwi0HhGOJlUrC9bmitCk0xa0+5zd6lw1Cx5o4M4xCmZQQfzxb9uo4H4MIH1MAkGA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgM4MIGXBgNVHR8EgY8wgYwwTKBKoEikRjBEMQ8wDQYDVQQDDAZZV0NBMDExETAPBgNVBAsMCENITkNURElEMREwDwYDVQQKDAhDSE5DVERJRDELMAkGA1UEBhMCQ04wPKA6oDiGNmh0dHA6Ly8xNzIuMjguMzAuODoxODA2MC9jYXdlYi9jcmwvWVdDQTAxL1lXQ0EwMV8wLmNybDAdBgNVHQ4EFgQU264a6MnK3/Ks3dVrI0BoEg2CLokwHwYDVR0jBBgwFoAUm1gG9DaAOEzQny6vTi6LlnTNgagwDAYIKoEcz1UBg3UFAANIADBFAiAPqpe2P76Eu0TBHKPqtflsCXTMUolq+FBXKK8nPhW9SwIhAKHGhPvqIAWcDHpu8xlHYJ9bkAwhJDDHNwspU7x6V0au' # 数据加密证书
app-name: '正在现场'
mode: 'R01'
# forward-url: 'http://authtest.ctdid.cn:10002/uentrance/interf/auth/request'
forward-url: 'http://auth.ctdid.cn:10002/uentrance/interf/auth/request'
\ No newline at end of file
package com.liquidnet.service.sweet.dto.admin;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@ApiModel(value = "SweetFormSubmissionActivityVo", description = "动态表单报名活动汇总")
public class SweetFormSubmissionActivityVo implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "活动标识(英文)")
private String activityName;
@ApiModelProperty(value = "活动名称(中文)")
private String activityLabel;
@ApiModelProperty(value = "报名数量")
private Long submissionCount;
@ApiModelProperty(value = "最早提交时间")
private LocalDateTime firstCreatedAt;
@ApiModelProperty(value = "最新提交时间")
private LocalDateTime lastCreatedAt;
}
package com.liquidnet.service.sweet.mapper; package com.liquidnet.service.sweet.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.liquidnet.service.sweet.dto.admin.SweetFormSubmissionActivityVo;
import com.liquidnet.service.sweet.entity.SweetFormSubmission; import com.liquidnet.service.sweet.entity.SweetFormSubmission;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/** /**
* 动态表单报名提交表 Mapper * 动态表单报名提交表 Mapper
*/ */
public interface SweetFormSubmissionMapper extends BaseMapper<SweetFormSubmission> { public interface SweetFormSubmissionMapper extends BaseMapper<SweetFormSubmission> {
List<SweetFormSubmissionActivityVo> selectActivityGroupList(@Param("activityName") String activityName);
List<SweetFormSubmission> selectForExport(@Param("activityName") String activityName,
@Param("beginTime") String beginTime,
@Param("endTime") String endTime);
} }
...@@ -2,4 +2,32 @@ ...@@ -2,4 +2,32 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liquidnet.service.sweet.mapper.SweetFormSubmissionMapper"> <mapper namespace="com.liquidnet.service.sweet.mapper.SweetFormSubmissionMapper">
<select id="selectActivityGroupList" resultType="com.liquidnet.service.sweet.dto.admin.SweetFormSubmissionActivityVo">
SELECT activity_name AS activityName,
COUNT(1) AS submissionCount,
MIN(created_at) AS firstCreatedAt,
MAX(created_at) AS lastCreatedAt
FROM sweet_form_submission
<where>
<if test="activityName != null and activityName != ''">
AND activity_name = #{activityName}
</if>
</where>
GROUP BY activity_name
ORDER BY MAX(created_at) DESC
</select>
<select id="selectForExport" resultType="com.liquidnet.service.sweet.entity.SweetFormSubmission">
SELECT mid, submission_id AS submissionId, activity_name AS activityName, data, created_at AS createdAt, updated_at AS updatedAt
FROM sweet_form_submission
WHERE activity_name = #{activityName}
<if test="beginTime != null and beginTime != ''">
AND created_at &gt;= #{beginTime}
</if>
<if test="endTime != null and endTime != ''">
AND created_at &lt;= #{endTime}
</if>
ORDER BY created_at ASC
</select>
</mapper> </mapper>
...@@ -6,7 +6,7 @@ create table adam_third_party ...@@ -6,7 +6,7 @@ create table adam_third_party
( (
mid bigint unsigned auto_increment primary key, mid bigint unsigned auto_increment primary key,
uid varchar(64) default '', uid varchar(64) default '',
open_id varchar(64) comment '第三方ID', open_id varchar(64) comment '第三方ID(NIA为56位标准Base64)',
avatar varchar(255) comment '第三方头像', avatar varchar(255) comment '第三方头像',
nickname varchar(100) comment '第三方昵称', nickname varchar(100) comment '第三方昵称',
platform varchar(20) comment '平台名称', platform varchar(20) comment '平台名称',
......
...@@ -77,6 +77,11 @@ ...@@ -77,6 +77,11 @@
<artifactId>liquidnet-common-third-shumei</artifactId> <artifactId>liquidnet-common-third-shumei</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency>
<groupId>com.liquidnet</groupId>
<artifactId>liquidnet-common-third-secure-access</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency> <dependency>
<groupId>com.aliyun</groupId> <groupId>com.aliyun</groupId>
......
...@@ -14,9 +14,11 @@ import com.liquidnet.commons.lang.constant.LnsEnum; ...@@ -14,9 +14,11 @@ import com.liquidnet.commons.lang.constant.LnsEnum;
import com.liquidnet.commons.lang.core.JwtValidator; import com.liquidnet.commons.lang.core.JwtValidator;
import com.liquidnet.commons.lang.util.*; import com.liquidnet.commons.lang.util.*;
import com.liquidnet.service.adam.constant.AdamConst; import com.liquidnet.service.adam.constant.AdamConst;
import com.liquidnet.service.adam.dto.AdamNiaLoginParam;
import com.liquidnet.service.adam.dto.AdamThirdPartParam; import com.liquidnet.service.adam.dto.AdamThirdPartParam;
import com.liquidnet.service.adam.dto.vo.AdamLoginInfoVo; import com.liquidnet.service.adam.dto.vo.AdamLoginInfoVo;
import com.liquidnet.service.adam.dto.vo.AdamUserInfoVo; import com.liquidnet.service.adam.dto.vo.AdamUserInfoVo;
import com.liquidnet.service.adam.service.IAdamNiaLoginService;
import com.liquidnet.service.adam.service.AdamRdmService; import com.liquidnet.service.adam.service.AdamRdmService;
import com.liquidnet.service.adam.service.AdamWechatService; import com.liquidnet.service.adam.service.AdamWechatService;
import com.liquidnet.service.adam.service.IAdamUserService; import com.liquidnet.service.adam.service.IAdamUserService;
...@@ -79,6 +81,8 @@ public class AdamLoginController { ...@@ -79,6 +81,8 @@ public class AdamLoginController {
AdamUserSessionSupport adamUserSessionSupport; AdamUserSessionSupport adamUserSessionSupport;
@Autowired @Autowired
SilentMobileOtpV2Support silentMobileOtpV2Support; SilentMobileOtpV2Support silentMobileOtpV2Support;
@Autowired
IAdamNiaLoginService adamNiaLoginService;
@Value("${liquidnet.reviewer.app-login.mobile}") @Value("${liquidnet.reviewer.app-login.mobile}")
private String reviewMobile; private String reviewMobile;
...@@ -328,6 +332,14 @@ public class AdamLoginController { ...@@ -328,6 +332,14 @@ public class AdamLoginController {
return this.loginVoResponseProcessing(loginInfoVo); return this.loginVoResponseProcessing(loginInfoVo);
} }
@ApiOperationSupport(order = 8)
@ApiOperation(value = "国家网络身份认证", notes = "仅做身份认证并返回 openId,前端拿到后调用 login/tpa 完成登录/注册绑定")
@PostMapping(value = {"login/nia"})
public ResponseDto<?> loginByNia(@Valid @RequestBody AdamNiaLoginParam param) {
log.info("login by nia, bizSeq={}", param.getBizSeq());
return adamNiaLoginService.login(param);
}
@ApiOperationSupport(order = 7) @ApiOperationSupport(order = 7)
@ApiOperation(value = "手机号静默登录") @ApiOperation(value = "手机号静默登录")
@ApiImplicitParams({ @ApiImplicitParams({
...@@ -522,19 +534,7 @@ public class AdamLoginController { ...@@ -522,19 +534,7 @@ public class AdamLoginController {
} }
private String ssoProcess(AdamUserInfoVo userInfoVo) { private String ssoProcess(AdamUserInfoVo userInfoVo) {
Map<String, Object> claimsMap = CollectionUtil.mapStringObject(); return adamUserSessionSupport.issueToken(userInfoVo);
claimsMap.put(CurrentUtil.TOKEN_SUB, userInfoVo.getUid());
claimsMap.put(CurrentUtil.TOKEN_MOBILE, userInfoVo.getMobile());
claimsMap.put(CurrentUtil.TOKEN_NICKNAME, userInfoVo.getNickname());
claimsMap.put(CurrentUtil.TOKEN_TYPE, CurrentUtil.TOKEN_TYPE_VAL_USER);
claimsMap.put(CurrentUtil.TOKEN_UCREATED, DateUtil.Formatter.yyyyMMddHHmmssTrim.format(userInfoVo.getCreateAt()));
log.debug("Gentoken:{}", claimsMap);
String jti = jwtValidator.generateJti();
String token = jwtValidator.create(claimsMap, jti);
adamUserSessionSupport.bindSession(jti, userInfoVo.getUid());
return token;
} }
private ResponseDto<AdamLoginInfoVo> loginVoResponseProcessing(AdamLoginInfoVo loginInfoVo) { private ResponseDto<AdamLoginInfoVo> loginVoResponseProcessing(AdamLoginInfoVo loginInfoVo) {
......
...@@ -365,7 +365,7 @@ public class AdamUserController { ...@@ -365,7 +365,7 @@ public class AdamUserController {
@ApiOperationSupport(order = 8) @ApiOperationSupport(order = 8)
@ApiOperation(value = "解绑第三方账号") @ApiOperation(value = "解绑第三方账号")
@ApiImplicitParams({ @ApiImplicitParams({
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "platform", value = "平台类型", allowableValues = "WEIBO,WECHAT,QQ"), @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "platform", value = "平台类型", allowableValues = "WEIBO,WECHAT,QQ,NIA"),
}) })
@PostMapping(value = {"tpa/unbind/{platform}"}) @PostMapping(value = {"tpa/unbind/{platform}"})
public ResponseDto<List<AdamThirdPartInfoVo>> unbindTpa(@Pattern(regexp = LnsRegex.Valid.TRIPLE_PF_FOR_ULGOIN, message = "平台类型无效") public ResponseDto<List<AdamThirdPartInfoVo>> unbindTpa(@Pattern(regexp = LnsRegex.Valid.TRIPLE_PF_FOR_ULGOIN, message = "平台类型无效")
......
package com.liquidnet.service.adam.service;
import com.liquidnet.service.adam.dto.AdamNiaLoginParam;
import com.liquidnet.service.base.ResponseDto;
public interface IAdamNiaLoginService {
ResponseDto<?> login(AdamNiaLoginParam param);
}
package com.liquidnet.service.adam.service.impl;
import com.liquidnet.common.third.secureaccess.biz.NiaAuthBiz;
import com.liquidnet.common.third.secureaccess.model.NiaAuthRequest;
import com.liquidnet.common.third.secureaccess.model.NiaAuthResult;
import com.liquidnet.common.third.secureaccess.util.NiaPidUtils;
import com.liquidnet.service.adam.constant.AdamTpaConst;
import com.liquidnet.service.adam.dto.AdamNiaLoginParam;
import com.liquidnet.service.adam.dto.vo.AdamNiaAuthVo;
import com.liquidnet.service.adam.service.IAdamNiaLoginService;
import com.liquidnet.service.base.ResponseDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class AdamNiaLoginServiceImpl implements IAdamNiaLoginService {
private final NiaAuthBiz niaAuthBiz;
@Override
public ResponseDto<?> login(AdamNiaLoginParam param) {
if (!niaAuthBiz.isReady()) {
return ResponseDto.failure("10005", "网证认证服务暂未开通,请稍后再试");
}
log.info("[nia-login] 请求 bizSeq={}, idCardAuthDataLen={}",
param.getBizSeq(), param.getIdCardAuthData() == null ? 0 : param.getIdCardAuthData().length());
NiaAuthResult authResult = niaAuthBiz.authenticate(NiaAuthRequest.builder()
.bizSeq(param.getBizSeq())
.idCardAuthData(param.getIdCardAuthData())
.build());
if (!authResult.isSuccess()) {
log.warn("[nia-login] 认证失败 bizSeq={}, stage={}, code={}, desc={}",
param.getBizSeq(),
authResult.getStage(),
authResult.getResultCode(),
authResult.getResultDesc());
String errMsg = authResult.getResultDesc() != null && !authResult.getResultDesc().isEmpty()
? authResult.getResultDesc() : "网证认证失败,请重试";
return ResponseDto.failure("10005", errMsg);
}
if (!NiaPidUtils.isValidOpenId(authResult.getOpenId())) {
log.warn("[nia-login] PID标识解析失败 bizSeq={}", param.getBizSeq());
return ResponseDto.failure("10005", "网证认证失败,请重试");
}
AdamNiaAuthVo authVo = AdamNiaAuthVo.builder()
.platform(AdamTpaConst.PLATFORM_NIA)
.openId(authResult.getOpenId())
.build();
log.info("[nia-login] 认证成功 bizSeq={}, bizSerialNo={}, openId={}",
param.getBizSeq(), authResult.getBizSerialNo(), authResult.getOpenId());
return ResponseDto.success(authVo);
}
}
...@@ -2,10 +2,16 @@ package com.liquidnet.service.adam.support; ...@@ -2,10 +2,16 @@ package com.liquidnet.service.adam.support;
import com.liquidnet.common.cache.redis.util.RedisUtil; import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.commons.lang.core.JwtValidator; import com.liquidnet.commons.lang.core.JwtValidator;
import com.liquidnet.commons.lang.util.CollectionUtil;
import com.liquidnet.commons.lang.util.CurrentUtil;
import com.liquidnet.commons.lang.util.DateUtil;
import com.liquidnet.service.adam.dto.vo.AdamUserInfoVo;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.Map;
/** /**
* 用户 JTI 会话:adam:session:{jti}=uid,adam:session:user:{uid} 记录活跃 jti。 * 用户 JTI 会话:adam:session:{jti}=uid,adam:session:user:{uid} 记录活跃 jti。
*/ */
...@@ -32,4 +38,17 @@ public class AdamUserSessionSupport { ...@@ -32,4 +38,17 @@ public class AdamUserSessionSupport {
redisUtil.setRemove(jwtValidator.userSessionsKey(uid), jti); redisUtil.setRemove(jwtValidator.userSessionsKey(uid), jti);
} }
} }
public String issueToken(AdamUserInfoVo userInfoVo) {
Map<String, Object> claimsMap = CollectionUtil.mapStringObject();
claimsMap.put(CurrentUtil.TOKEN_SUB, userInfoVo.getUid());
claimsMap.put(CurrentUtil.TOKEN_MOBILE, userInfoVo.getMobile());
claimsMap.put(CurrentUtil.TOKEN_NICKNAME, userInfoVo.getNickname());
claimsMap.put(CurrentUtil.TOKEN_TYPE, CurrentUtil.TOKEN_TYPE_VAL_USER);
claimsMap.put(CurrentUtil.TOKEN_UCREATED, DateUtil.Formatter.yyyyMMddHHmmssTrim.format(userInfoVo.getCreateAt()));
String jti = jwtValidator.generateJti();
String token = jwtValidator.create(claimsMap, jti);
bindSession(jti, userInfoVo.getUid());
return token;
}
} }
package com.liquidnet.service.goblin.controller.manage; package com.liquidnet.service.goblin.controller.manage;
import com.github.pagehelper.PageInfo; import com.github.xiaoymin.knife4j.annotations.ApiSupport;import com.liquidnet.service.base.ResponseDto;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinBackOrderDetailsVo; import com.liquidnet.service.goblin.dto.vo.GoblinBackOrderDetailsVo;
import com.liquidnet.service.goblin.dto.vo.GoblinBackOrderVo; import com.liquidnet.service.goblin.dto.vo.GoblinStoreBackOrderListPageVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreBackOrderListVo; import com.liquidnet.service.goblin.dto.vo.GoblinStoreBackOrderListVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreOrderListVo; import com.liquidnet.service.goblin.param.GoblinStoreOrderRefundParam;
import com.liquidnet.service.goblin.param.RefundCallbackParam;
import com.liquidnet.service.goblin.service.manage.IGoblinStoreBackOrderService; import com.liquidnet.service.goblin.service.manage.IGoblinStoreBackOrderService;
import com.liquidnet.service.goblin.service.manage.IGoblinStoreOrderService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiImplicitParams;
...@@ -41,17 +37,17 @@ public class GoblinStoreBackOrderController { ...@@ -41,17 +37,17 @@ public class GoblinStoreBackOrderController {
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "cet", value = "申请终止时间"), @ApiImplicitParam(type = "form", required = false, dataType = "String", name = "cet", value = "申请终止时间"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "orderCode", value = "订单编号"), @ApiImplicitParam(type = "form", required = false, dataType = "String", name = "orderCode", value = "订单编号"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "spuName", value = "商品名称"), @ApiImplicitParam(type = "form", required = false, dataType = "String", name = "spuName", value = "商品名称"),
@ApiImplicitParam(type = "form", required = false, dataType = "Integer", name = "status", value = "订单状态[1-待处理|2-已退款|9-已拒绝|11-已取消|0-退款中|7-退货中]"), @ApiImplicitParam(type = "form", required = false, dataType = "String", name = "status", value = "审核状态,多个用英文逗号分隔。0-商铺发起退款|1-退款申请|2-退款成功|3-退款拒绝|4-退货申请|5-退货拒绝|6-待填物流|7-待收货|8-退货完成|9-退货失败|10-退款失败|11-取消退款"),
}) })
@GetMapping(value = "list") @GetMapping(value = "list")
public ResponseDto<PageInfo<GoblinStoreBackOrderListVo>> orderBackList(@RequestParam(value = "page", required = true) @Valid Integer page, public ResponseDto<GoblinStoreBackOrderListPageVo> orderBackList(@RequestParam(value = "page", required = true) @Valid Integer page,
@RequestParam(value = "orderBackCode", required = false) String orderBackCode, @RequestParam(value = "orderBackCode", required = false) String orderBackCode,
@RequestParam(value = "type", required = false) Integer type, @RequestParam(value = "type", required = false) Integer type,
@RequestParam(value = "cst", required = false) String cst, @RequestParam(value = "cst", required = false) String cst,
@RequestParam(value = "cet", required = false) String cet, @RequestParam(value = "cet", required = false) String cet,
@RequestParam(value = "orderCode", required = false) String orderCode, @RequestParam(value = "orderCode", required = false) String orderCode,
@RequestParam(value = "spuName", required = false) String spuName, @RequestParam(value = "spuName", required = false) String spuName,
@RequestParam(value = "status", required = false) Integer status) { @RequestParam(value = "status", required = false) String status) {
return goblinStoreBackOrderService.orderBackList(page, orderBackCode, type, cst, cet, orderCode, spuName, status); return goblinStoreBackOrderService.orderBackList(page, orderBackCode, type, cst, cet, orderCode, spuName, status);
} }
...@@ -83,6 +79,12 @@ public class GoblinStoreBackOrderController { ...@@ -83,6 +79,12 @@ public class GoblinStoreBackOrderController {
return goblinStoreBackOrderService.refusedRefund(backOrderId); return goblinStoreBackOrderService.refusedRefund(backOrderId);
} }
@ApiOperation(value = "发起退款")
@PostMapping(value = "refund")
public ResponseDto<Boolean> refundOrder(@RequestBody @Valid GoblinStoreOrderRefundParam param) {
return goblinStoreBackOrderService.refundOrder(param);
}
@ApiOperation(value = "修改金额") @ApiOperation(value = "修改金额")
@ApiImplicitParams({ @ApiImplicitParams({
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "backOrderId", value = "订单id"), @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "backOrderId", value = "订单id"),
......
package com.liquidnet.service.goblin.controller.manage; package com.liquidnet.service.goblin.controller.manage;
import com.github.pagehelper.PageInfo;
import com.github.xiaoymin.knife4j.annotations.ApiSupport; import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.liquidnet.commons.lang.util.CurrentUtil; import com.liquidnet.commons.lang.util.CurrentUtil;
import com.liquidnet.service.base.ResponseDto; import com.liquidnet.service.base.ResponseDto;
import com.liquidnet.service.goblin.dto.vo.GoblinMailVo; import com.liquidnet.service.goblin.dto.vo.GoblinStoreOrderListPageVo;
import com.liquidnet.service.goblin.dto.vo.GoblinStoreOrderListVo; import com.liquidnet.service.goblin.dto.vo.GoblinStoreOrderListVo;
import com.liquidnet.service.goblin.param.RefundCallbackParam;
import com.liquidnet.service.goblin.service.IGoblinOrderService;
import com.liquidnet.service.goblin.service.manage.IGoblinStoreOrderService; import com.liquidnet.service.goblin.service.manage.IGoblinStoreOrderService;
import io.swagger.annotations.*; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid; import javax.validation.Valid;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List;
@ApiSupport(order = 149101) @ApiSupport(order = 149101)
@Api(tags = "店铺-订单") @Api(tags = "店铺-订单")
...@@ -40,10 +43,10 @@ public class GoblinStoreOrderController { ...@@ -40,10 +43,10 @@ public class GoblinStoreOrderController {
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "cet", value = "下单终止时间"), @ApiImplicitParam(type = "form", required = false, dataType = "String", name = "cet", value = "下单终止时间"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "expressContacts", value = "收货人姓名"), @ApiImplicitParam(type = "form", required = false, dataType = "String", name = "expressContacts", value = "收货人姓名"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "phone", value = "手机号"), @ApiImplicitParam(type = "form", required = false, dataType = "String", name = "phone", value = "手机号"),
@ApiImplicitParam(type = "form", required = false, dataType = "Integer", name = "status", value = "订单状态[0-代付款|2-待发货|3-已发货|4-已完成]"), @ApiImplicitParam(type = "form", required = false, dataType = "Integer", name = "status", value = "订单状态[0-待支付|2-待发货|3-已发货|4-已完成|6-已退款]"),
}) })
@GetMapping(value = "list") @GetMapping(value = "list")
public ResponseDto<PageInfo<GoblinStoreOrderListVo>> orderList(@RequestParam(value = "page", required = true) @Valid Integer page, public ResponseDto<GoblinStoreOrderListPageVo> orderList(@RequestParam(value = "page", required = true) @Valid Integer page,
@RequestParam(value = "orderCode", required = false) String orderCode, @RequestParam(value = "orderCode", required = false) String orderCode,
@RequestParam(value = "type", required = false) Integer type, @RequestParam(value = "type", required = false) Integer type,
@RequestParam(value = "cst", required = false) String cst, @RequestParam(value = "cst", required = false) String cst,
...@@ -55,6 +58,26 @@ public class GoblinStoreOrderController { ...@@ -55,6 +58,26 @@ public class GoblinStoreOrderController {
return goblinStoreOrderService.orderList(page, orderCode, type, cst, cet, expressContacts, phone, status); return goblinStoreOrderService.orderList(page, orderCode, type, cst, cet, expressContacts, phone, status);
} }
@ApiOperation(value = "订单列表导出")
@ApiImplicitParams({
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "orderCode", value = "订单编号"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "cst", value = "下单起始时间"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "cet", value = "下单终止时间"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "expressContacts", value = "收货人姓名"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "phone", value = "手机号"),
@ApiImplicitParam(type = "form", required = false, dataType = "Integer", name = "status", value = "订单状态[0-代付款|2-待发货|3-已发货|4-已完成]"),
})
@GetMapping(value = "list/export")
public void exportOrderList(HttpServletResponse response,
@RequestParam(value = "orderCode", required = false) String orderCode,
@RequestParam(value = "cst", required = false) String cst,
@RequestParam(value = "cet", required = false) String cet,
@RequestParam(value = "expressContacts", required = false) String expressContacts,
@RequestParam(value = "phone", required = false) String phone,
@RequestParam(value = "status", required = false) Integer status) {
goblinStoreOrderService.exportOrderList(response, orderCode, cst, cet, expressContacts, phone, status);
}
@ApiOperation(value = "订单详情") @ApiOperation(value = "订单详情")
@ApiImplicitParams({ @ApiImplicitParams({
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "orderId", value = "订单id"), @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "orderId", value = "订单id"),
...@@ -112,7 +135,7 @@ public class GoblinStoreOrderController { ...@@ -112,7 +135,7 @@ public class GoblinStoreOrderController {
return goblinStoreOrderService.changeAddress(orderId, expressContacts, expressPhone, expressAddressDetail); return goblinStoreOrderService.changeAddress(orderId, expressContacts, expressPhone, expressAddressDetail);
} }
@ApiOperation(value = "退款(sku/整单)") @ApiOperation(value = "退款(sku/整单)-废弃")
@ApiImplicitParams({ @ApiImplicitParams({
@ApiImplicitParam(type = "form", required = true, dataType = "String", name = "orderId", value = "订单id"), @ApiImplicitParam(type = "form", required = true, dataType = "String", name = "orderId", value = "订单id"),
@ApiImplicitParam(type = "form", required = false, dataType = "String", name = "orderSkuId", value = "orderSkuId[整单退不传]"), @ApiImplicitParam(type = "form", required = false, dataType = "String", name = "orderSkuId", value = "orderSkuId[整单退不传]"),
...@@ -135,7 +158,7 @@ public class GoblinStoreOrderController { ...@@ -135,7 +158,7 @@ public class GoblinStoreOrderController {
public ResponseDto<Boolean> express(@RequestParam(value = "orderId", required = true) @Valid String orderId, public ResponseDto<Boolean> express(@RequestParam(value = "orderId", required = true) @Valid String orderId,
@RequestParam(value = "orderSkuIds", required = false) @Valid String orderSkuIds, @RequestParam(value = "orderSkuIds", required = false) @Valid String orderSkuIds,
@RequestParam(value = "mailNo", required = true) @Valid String mailNo) { @RequestParam(value = "mailNo", required = true) @Valid String mailNo) {
return goblinStoreOrderService.express(orderId, orderSkuIds, mailNo, CurrentUtil.getCurrentUid(),""); return goblinStoreOrderService.express(orderId, orderSkuIds, mailNo, CurrentUtil.getCurrentUid(), "");
} }
@ApiOperation(value = "修改快递单号") @ApiOperation(value = "修改快递单号")
......
...@@ -129,6 +129,7 @@ goblin_order.store.log=INSERT INTO goblin_order_operation_log (`order_log_id`,`o ...@@ -129,6 +129,7 @@ goblin_order.store.log=INSERT INTO goblin_order_operation_log (`order_log_id`,`o
goblin_order.store.refundLog=INSERT INTO goblin_back_order_log (`back_order_log_id`,`back_order_id`,`operation_type`,`message`,`operation_name`,`status`,`created_at`) VALUES(?,?,?,?,?,?,?) goblin_order.store.refundLog=INSERT INTO goblin_back_order_log (`back_order_log_id`,`back_order_id`,`operation_type`,`message`,`operation_name`,`status`,`created_at`) VALUES(?,?,?,?,?,?,?)
goblin_order.store.orderExpressPrice=UPDATE goblin_store_order SET price_total =? , price_modify = ? ,price_voucher = ? , price_actual = ? ,price_express = ? , updated_at = ? WHERE order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null) goblin_order.store.orderExpressPrice=UPDATE goblin_store_order SET price_total =? , price_modify = ? ,price_voucher = ? , price_actual = ? ,price_express = ? , updated_at = ? WHERE order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
goblin_order.store.backOrder=INSERT INTO goblin_back_order (`back_order_id`,`back_code`,`order_id`,`order_code`,`store_id`,`user_id`,`sku_id_nums`,`type`,`reason`,`describes`,`real_back_price`,`status`,`created_at`,`audit_at`,`error_reason`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) goblin_order.store.backOrder=INSERT INTO goblin_back_order (`back_order_id`,`back_code`,`order_id`,`order_code`,`store_id`,`user_id`,`sku_id_nums`,`type`,`reason`,`describes`,`real_back_price`,`status`,`created_at`,`audit_at`,`error_reason`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
goblin_order.store.backOrderWithExpress=INSERT INTO goblin_back_order (`back_order_id`,`back_code`,`order_id`,`order_code`,`store_id`,`user_id`,`sku_id_nums`,`type`,`reason`,`describes`,`real_back_price`,`back_price_express`,`status`,`pics`,`created_at`,`audit_at`,`error_reason`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
goblin_order.store.changeExpress=UPDATE goblin_back_order SET real_back_price = ? ,back_price_express = ? updated_at = ? WHERE back_order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null) goblin_order.store.changeExpress=UPDATE goblin_back_order SET real_back_price = ? ,back_price_express = ? updated_at = ? WHERE back_order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
goblin_order.store.changeSku=UPDATE goblin_back_order SET real_back_price = ? , updated_at = ? WHERE back_order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null) goblin_order.store.changeSku=UPDATE goblin_back_order SET real_back_price = ? , updated_at = ? WHERE back_order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
goblin_order.store.backOrderStatus=UPDATE goblin_back_order SET status = ? , refuse_at=?,refuse_size=?,updated_at = ? WHERE back_order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null) goblin_order.store.backOrderStatus=UPDATE goblin_back_order SET status = ? , refuse_at=?,refuse_size=?,updated_at = ? WHERE back_order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
...@@ -137,6 +138,7 @@ goblin_order.store.orderStatus.time=UPDATE goblin_store_order SET status = ? ,zh ...@@ -137,6 +138,7 @@ goblin_order.store.orderStatus.time=UPDATE goblin_store_order SET status = ? ,zh
goblin_order.store.applyRefund=UPDATE goblin_back_order SET status = ? ,reason=?,audit_at=?, updated_at = ? WHERE back_order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null) goblin_order.store.applyRefund=UPDATE goblin_back_order SET status = ? ,reason=?,audit_at=?, updated_at = ? WHERE back_order_id = ? and (updated_at <= ? or created_at = ? or updated_at is null)
#---- \u7528\u6237\u8BA2\u5355\u64CD\u4F5C #---- \u7528\u6237\u8BA2\u5355\u64CD\u4F5C
goblin_order.user.applyRefund=INSERT INTO goblin_back_order (`back_order_id`,`back_code`,`order_id`,`order_code`,`store_id`,`user_id`,`sku_id_nums`,`type`,`reason`,`describes`,`real_back_price`,`back_price_express`,`status`,`logis_company_name`,`mail_no`,`pics`,`created_at`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) goblin_order.user.applyRefund=INSERT INTO goblin_back_order (`back_order_id`,`back_code`,`order_id`,`order_code`,`store_id`,`user_id`,`sku_id_nums`,`type`,`reason`,`describes`,`real_back_price`,`back_price_express`,`status`,`logis_company_name`,`mail_no`,`pics`,`created_at`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
goblin_order.user.applyRefundAuto=INSERT INTO goblin_back_order (`back_order_id`,`back_code`,`order_id`,`order_code`,`store_id`,`user_id`,`sku_id_nums`,`type`,`reason`,`describes`,`real_back_price`,`back_price_express`,`status`,`logis_company_name`,`mail_no`,`pics`,`created_at`,`audit_at`,`error_reason`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
goblin_order.user.againRefund=UPDATE goblin_back_order SET status = ? , describes = ? , pics = ? , reason = ? , type = ?, updated_at=? where back_order_id=? goblin_order.user.againRefund=UPDATE goblin_back_order SET status = ? , describes = ? , pics = ? , reason = ? , type = ?, updated_at=? where back_order_id=?
#---- \u8D2D\u7269\u8F66\u64CD\u4F5C #---- \u8D2D\u7269\u8F66\u64CD\u4F5C
goblin_shop.cart.delete=UPDATE goblin_shopping_cart set del_tag=? where user_id=? and sku_id=? goblin_shop.cart.delete=UPDATE goblin_shopping_cart set del_tag=? where user_id=? and sku_id=?
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment