Feat: Api 联通 + 地区整理

This commit is contained in:
Owen 2024-12-27 10:41:14 +08:00
parent 8ac418984a
commit 80ad261000
13 changed files with 3545 additions and 35 deletions

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.haoka.api;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.haoka.dal.dataobject.superiorapi.SuperiorApiDevConfigDO;
import cn.iocoder.yudao.module.haoka.service.superiorapi.SuperiorApiDevConfigService;
import cn.iocoder.yudao.module.haoka.service.superiorapi.SuperiorApiService;
import jakarta.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public abstract class ApiConfigService {
@Resource
protected SuperiorApiService superiorApiService;
@Resource
protected SuperiorApiDevConfigService superiorApiDevConfigService;
protected Map<String, String> getDevConfig(ApiFrom apiFrom) {
List<SuperiorApiDevConfigDO> list = superiorApiDevConfigService.list(
new LambdaQueryWrapperX<SuperiorApiDevConfigDO>()
.eqIfPresent(SuperiorApiDevConfigDO::getHaokaSuperiorApiId, apiFrom.getId())
);
return list.stream().collect(Collectors.toMap(SuperiorApiDevConfigDO::getCode, SuperiorApiDevConfigDO::getValue));
}
}

View File

@ -6,7 +6,8 @@ import lombok.Getter;
@AllArgsConstructor
@Getter
public enum ApiFrom {
LianTong(1L,"联通Api");
LianTong(1L,"联通Api"),
GuangZhouDX(2L,"广州电信Api");;
private final Long id;
private final String name;
}

View File

@ -0,0 +1,46 @@
package cn.iocoder.yudao.module.haoka.api;
import cn.iocoder.yudao.module.haoka.api.guangdong.GdOrderCreateRequestParam;
import cn.iocoder.yudao.module.haoka.api.guangdong.inner.GdDxApi;
import cn.iocoder.yudao.module.haoka.api.guangdong.inner.GdOrderQueryRequestParam;
import com.mashape.unirest.http.HttpResponse;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class ApiGuangZhouDXService extends ApiConfigService {
public static final String Key_urlPrefix = "urlPrefix";
public static final String Key_publicKey = "publicKey";
public static final String Path_createOrder = "";
public String createOrder(GdOrderCreateRequestParam param) {
Map<String, String> devConfig = getDevConfig(ApiFrom.GuangZhouDX);
String urlPrefix = devConfig.get(Key_urlPrefix);
String publicKey = devConfig.get(Key_publicKey);
try {
HttpResponse<String> order = GdDxApi.createOrder(urlPrefix + Path_createOrder, publicKey, param);
return order.getBody();
} catch (Exception ignored) {
}
return "";
}
public String queryOrder(GdOrderQueryRequestParam param) {
Map<String, String> devConfig = getDevConfig(ApiFrom.GuangZhouDX);
String urlPrefix = devConfig.get(Key_urlPrefix);
String publicKey = devConfig.get(Key_publicKey);
try {
HttpResponse<String> order = GdDxApi.queryOrder(urlPrefix + Path_createOrder, publicKey, param);
return order.getBody();
} catch (Exception ignored) {
}
return "";
}
}

View File

@ -1,37 +1,15 @@
package cn.iocoder.yudao.module.haoka.api;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.haoka.api.liantong.LianTongApiUtil;
import cn.iocoder.yudao.module.haoka.api.liantong.client.ZopClient;
import cn.iocoder.yudao.module.haoka.api.liantong.model.ZopResponse;
import cn.iocoder.yudao.module.haoka.api.liantong.model.request.*;
import cn.iocoder.yudao.module.haoka.api.liantong.model.response.*;
import cn.iocoder.yudao.module.haoka.dal.dataobject.superiorapi.SuperiorApiDevConfigDO;
import cn.iocoder.yudao.module.haoka.service.superiorapi.SuperiorApiDevConfigService;
import cn.iocoder.yudao.module.haoka.service.superiorapi.SuperiorApiService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class HaokaAllSuperiorApiService {
@Resource
private SuperiorApiService superiorApiService;
@Resource
private SuperiorApiDevConfigService superiorApiDevConfigService;
private Map<String, String> getDevConfig(ApiFrom apiFrom) {
List<SuperiorApiDevConfigDO> list = superiorApiDevConfigService.list(
new LambdaQueryWrapperX<SuperiorApiDevConfigDO>()
.eqIfPresent(SuperiorApiDevConfigDO::getHaokaSuperiorApiId, apiFrom.getId())
);
return list.stream().collect(Collectors.toMap(SuperiorApiDevConfigDO::getCode, SuperiorApiDevConfigDO::getValue));
}
public class ApiLianTongService extends ApiConfigService {
private ZopClient lianTongZopClient(ApiFrom apiFrom) {
Map<String, String> devConfig = this.getDevConfig(apiFrom);

View File

@ -22,11 +22,11 @@ public class GdDxApi {
"OrcAVMN/vpXTN2dCLGZXQrsTK9sAWASzIWTiQRzmnwxsQY7y4u2h+k41n8Z5tw03" +
"wwIDAQAB";
public static HttpResponse<String> createOrder(GdOrderCreateRequestParam param, String url, String publicKey) throws Exception {
public static HttpResponse<String> createOrder(String url, String publicKey, GdOrderCreateRequestParam param) throws Exception {
GdOrderCreateRequestParamCn gdOrderCreateRequestParamCn = OrderDataConverter.convertToCN(param);
Map<String,GdOrderCreateRequestParamCn> map =new HashMap<>(1);
Map<String, GdOrderCreateRequestParamCn> map = new HashMap<>(1);
map.put("order_data",gdOrderCreateRequestParamCn);
map.put("order_data", gdOrderCreateRequestParamCn);
String jsonString = JSON.toJSONString(map);
String sign = GdDxApiSign.sign(jsonString, publicKey);
@ -37,7 +37,7 @@ public class GdDxApi {
.asString();
}
public static HttpResponse<String> queryOrder(GdOrderQueryRequestParam param, String url, String publicKey) throws Exception {
public static HttpResponse<String> queryOrder(String url, String publicKey, GdOrderQueryRequestParam param) throws Exception {
String beforeSignStr = param.toBeforeSignStr();
String sign = param.getSign(publicKey);
return Unirest.get(url + beforeSignStr)
@ -48,16 +48,16 @@ public class GdDxApi {
public static void main(String[] args) throws Exception {
String data = "{\"order_data\":{\"代理商账号\":\"代理商账号\",\"使用人姓名\":\"使用人姓名\",\"使用人证件号\":\"370102199003072994\",\"业务类型\":\"移动业务\",\"办理类型\":\"新装主卡\",\"发票信息\":{\"发票投递方式\":\"不需要\",\"发票抬头\":\"\",\"电子邮箱\":\"\"},\"客户信息\":{\"客户类型\":\"普通客户\",\"客户证件号码\":\"445121199609133***\",\"客户证件姓名\":\"曾锦鑫\",\"客户证件类型\":\"身份证\",\"联系人\":\"曾锦鑫\",\"联系电话\":\"17807688***\"},\"接口下单分发账号\":\"\",\"接口下单分发账号名称\":\"\",\"接口下单账号\":\"pcesurf\",\"揽装信息\":{\"协销工号\":\"\",\"揽装工号\":\"30296079\"},\"一次性费用项\":{\"费用项列表\":[{\"缴费方式\":\"穗易付\",\"费用项名称\":\"预存50得100\",\"费用项类型\":\"\",\"费用项金额\":\"50\"}]},\"月租费用项\":{\"缴费方式\":\"现金支付\",\"费用项列表\":[{\"费用项名称\":\"(AI受理)19元星卡+3G+9元通行证\"}]},\"物流信息\":{\"发货类型\":\"同德仓发货\",\"受理类型\":\"后置受理\",\"回收资料\":\"\",\"配送公司\":\"EMS\",\"配送失败是否需要回退\":\"\",\"配送类型\":\"高值服务\"},\"移动接入\":[{\"UIM实物串号\":\"\",\"号码类型\":\"主卡\",\"移动接入号\":\"随机\",\"订购类型\":\"新装\"}],\"订单备注\":\"\",\"订单来源单号\":\"\",\"订购产品\":{\"AI编码\":\"DE58674702734165B55F3BFE402813C5\",\"产品名称\":\"19元星卡+3G+9元通行证\",\"付费类型\":\"预付费\",\"属性列表\":[{\"属性值\":\"\",\"属性名\":\"\"}]},\"配送信息\":{\"\":\"潮安县\",\"\":\"潮州市\",\"\":\"广东省\",\"联系人\":\"曾锦鑫\",\"联系电话\":\"17807688***\",\"详细地址\":\"凤塘镇南门村老农机对面惠信手机\",\"配送产品\":[{\"产品名称\":\"实名卡套YXS471\",\"产品数量\":\"1\",\"产品类型\":\"礼品\"}]}}}";System.out.println(data);
String data = "{\"order_data\":{\"代理商账号\":\"代理商账号\",\"使用人姓名\":\"使用人姓名\",\"使用人证件号\":\"370102199003072994\",\"业务类型\":\"移动业务\",\"办理类型\":\"新装主卡\",\"发票信息\":{\"发票投递方式\":\"不需要\",\"发票抬头\":\"\",\"电子邮箱\":\"\"},\"客户信息\":{\"客户类型\":\"普通客户\",\"客户证件号码\":\"445121199609133***\",\"客户证件姓名\":\"曾锦鑫\",\"客户证件类型\":\"身份证\",\"联系人\":\"曾锦鑫\",\"联系电话\":\"17807688***\"},\"接口下单分发账号\":\"\",\"接口下单分发账号名称\":\"\",\"接口下单账号\":\"pcesurf\",\"揽装信息\":{\"协销工号\":\"\",\"揽装工号\":\"30296079\"},\"一次性费用项\":{\"费用项列表\":[{\"缴费方式\":\"穗易付\",\"费用项名称\":\"预存50得100\",\"费用项类型\":\"\",\"费用项金额\":\"50\"}]},\"月租费用项\":{\"缴费方式\":\"现金支付\",\"费用项列表\":[{\"费用项名称\":\"(AI受理)19元星卡+3G+9元通行证\"}]},\"物流信息\":{\"发货类型\":\"同德仓发货\",\"受理类型\":\"后置受理\",\"回收资料\":\"\",\"配送公司\":\"EMS\",\"配送失败是否需要回退\":\"\",\"配送类型\":\"高值服务\"},\"移动接入\":[{\"UIM实物串号\":\"\",\"号码类型\":\"主卡\",\"移动接入号\":\"随机\",\"订购类型\":\"新装\"}],\"订单备注\":\"\",\"订单来源单号\":\"\",\"订购产品\":{\"AI编码\":\"DE58674702734165B55F3BFE402813C5\",\"产品名称\":\"19元星卡+3G+9元通行证\",\"付费类型\":\"预付费\",\"属性列表\":[{\"属性值\":\"\",\"属性名\":\"\"}]},\"配送信息\":{\"\":\"潮安县\",\"\":\"潮州市\",\"\":\"广东省\",\"联系人\":\"曾锦鑫\",\"联系电话\":\"17807688***\",\"详细地址\":\"凤塘镇南门村老农机对面惠信手机\",\"配送产品\":[{\"产品名称\":\"实名卡套YXS471\",\"产品数量\":\"1\",\"产品类型\":\"礼品\"}]}}}";
System.out.println(data);
System.out.println(data);
// GdOrderCreateRequestParamCn create = JSON.parseObject(data, GdOrderCreateRequestParamCn.class);
GdOrderCreateRequestParam param = getParam();
HttpResponse<String> order = createOrder("https://applet.mini189.cn/pgprod/generate?turbo=1", publicKey,param);
GdOrderCreateRequestParam param = getParam();
HttpResponse<String> order = createOrder(param, "https://applet.mini189.cn/pgprod/generate?turbo=1", publicKey);
// HttpResponse<String> order = queryOrder(new GdOrderQueryRequestParam(), "https://applet.mini189.cn/pgprod/query/query-order-info?", publicKey);
// HttpResponse<String> order = queryOrder("https://applet.mini189.cn/pgprod/query/query-order-info?", publicKey ,new GdOrderQueryRequestParam() );
// System.out.println(publicKey);
System.out.println("getStatus" + order.getStatus());

View File

@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.haoka.api.hunan;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
import java.util.TreeMap;
public class HunanDXApi {
public static String genSignFromMap(JSONObject data, String seckey) throws Exception {
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> mapBody = mapper.readValue(data.toString(), Map.class);
Map<String, String> params = (Map<String,String>) mapBody.get("data");
Map<String, String> map = new TreeMap<String, String>();
for (Map.Entry<String, String> entry : params.entrySet()) {
if(entry.getValue()==null||entry.getValue().equals("")){
continue;
}
if (!entry.getKey().equals("sign")) {
map.put(entry.getKey(), entry.getValue());
}
}
StringBuffer sb = new StringBuffer();
int index = 0;
for(String _key:map.keySet()){
if(index!=0){
sb.append("&");
}
sb.append(_key);
sb.append("=");
sb.append(map.get(_key));
index++;
}
sb.append("&key=" + seckey);
String md5 = MD5Utils.md5Digest(sb.toString()).toLowerCase();
return md5;
}
}

View File

@ -0,0 +1,53 @@
package cn.iocoder.yudao.module.haoka.api.hunan;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5Utils {
/**
* 生成 MD5 哈希值
*
* @param data 输入的字符串
* @return 32 位的 MD5 哈希值小写
*/
public static String md5Digest(String data) {
try {
// 获取 MD5 实例
MessageDigest md = MessageDigest.getInstance("MD5");
// 将输入字符串转换为字节数组
byte[] inputBytes = data.getBytes();
// 计算 MD5 哈希值
byte[] hashBytes = md.digest(inputBytes);
// 将字节数组转换为十六进制字符串
return bytesToHex(hashBytes);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("MD5 algorithm not found", e);
}
}
/**
* 将字节数组转换为十六进制字符串
*
* @param bytes 字节数组
* @return 十六进制字符串
*/
private static String bytesToHex(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
// 测试方法
public static void main(String[] args) {
String data = "Hello, World!";
String sign = MD5Utils.md5Digest(data);
System.out.println("MD5: " + sign);
}
}

View File

@ -0,0 +1,60 @@
package cn.iocoder.yudao.module.haoka.api.liantong.area;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.util.List;
/**
* 区域节点包括国家省份城市地区等信息
*
* 数据可见 resources/area.csv 文件
*
* @author 芋道源码
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString(exclude = {"parent"}) // 参见 https://gitee.com/yudaocode/yudao-cloud-mini/pulls/2 原因
public class Area {
/**
* 编号 - 全球即根目录
*/
public static final Integer ID_GLOBAL = 0;
/**
* 编号 - 中国
*/
public static final Integer ID_CHINA = 1;
/**
* 编号
*/
private Integer id;
/**
* 名字
*/
private String name;
/**
* 类型
*
* 枚举 {@link AreaTypeEnum}
*/
private Integer type;
/**
* 父节点
*/
@JsonManagedReference
private Area parent;
/**
* 子节点
*/
@JsonBackReference
private List<Area> children;
}

View File

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.haoka.api.liantong.area;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 区域类型枚举
*
* @author 芋道源码
*/
@AllArgsConstructor
@Getter
public enum AreaTypeEnum implements IntArrayValuable {
COUNTRY(1, "国家"),
PROVINCE(2, "省份"),
CITY(3, "城市"),
DISTRICT(4, "地区"), // 区等
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AreaTypeEnum::getType).toArray();
/**
* 类型
*/
private final Integer type;
/**
* 名字
*/
private final String name;
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@ -0,0 +1,212 @@
package cn.iocoder.yudao.module.haoka.api.liantong.area;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.csv.CsvRow;
import cn.hutool.core.text.csv.CsvUtil;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.findFirst;
/**
* 区域工具类
*
* @author 芋道源码
*/
@Slf4j
public class AreaUtils {
/**
* 初始化 SEARCHER
*/
@SuppressWarnings("InstantiationOfUtilityClass")
private final static AreaUtils INSTANCE = new AreaUtils();
/**
* Area 内存缓存提升访问速度
*/
private static Map<Integer, Area> areas;
private AreaUtils() {
long now = System.currentTimeMillis();
areas = new HashMap<>();
areas.put(Area.ID_GLOBAL, new Area(Area.ID_GLOBAL, "全球", 0,
null, new ArrayList<>()));
// csv 中加载数据
List<CsvRow> rows = CsvUtil.getReader().read(ResourceUtil.getUtf8Reader("area.csv")).getRows();
rows.remove(0); // 删除 header
for (CsvRow row : rows) {
// 创建 Area 对象
Area area = new Area(Integer.valueOf(row.get(0)), row.get(1), Integer.valueOf(row.get(2)),
null, new ArrayList<>());
// 添加到 areas
areas.put(area.getId(), area);
}
// 构建父子关系因为 Area 中没有 parentId 字段所以需要重复读取
for (CsvRow row : rows) {
Area area = areas.get(Integer.valueOf(row.get(0))); // 自己
Area parent = areas.get(Integer.valueOf(row.get(3))); //
Assert.isTrue(area != parent, "{}:父子节点相同", area.getName());
area.setParent(parent);
parent.getChildren().add(area);
}
log.info("启动加载 AreaUtils 成功,耗时 ({}) 毫秒", System.currentTimeMillis() - now);
}
/**
* 获得指定编号对应的区域
*
* @param id 区域编号
* @return 区域
*/
public static Area getArea(Integer id) {
return areas.get(id);
}
/**
* 获得指定区域对应的编号
*
* @param pathStr 区域路径例如说河南省/石家庄市/新华区
* @return 区域
*/
public static Area parseArea(String pathStr) {
String[] paths = pathStr.split("/");
Area area = null;
for (String path : paths) {
if (area == null) {
area = findFirst(areas.values(), item -> item.getName().equals(path));
} else {
area = findFirst(area.getChildren(), item -> item.getName().equals(path));
}
}
return area;
}
/**
* 获取所有节点的全路径名称如河南省/石家庄市/新华区
*
* @param areas 地区树
* @return 所有节点的全路径名称
*/
public static List<String> getAreaNodePathList(List<Area> areas) {
List<String> paths = new ArrayList<>();
areas.forEach(area -> getAreaNodePathList(area, "", paths));
return paths;
}
/**
* 构建一棵树的所有节点的全路径名称并将其存储为 "祖先/父级/子级" 的形式
*
* @param node 父节点
* @param path 全路径名称
* @param paths 全路径名称列表省份/城市/地区
*/
private static void getAreaNodePathList(Area node, String path, List<String> paths) {
if (node == null) {
return;
}
// 构建当前节点的路径
String currentPath = path.isEmpty() ? node.getName() : path + "/" + node.getName();
paths.add(currentPath);
// 递归遍历子节点
for (Area child : node.getChildren()) {
getAreaNodePathList(child, currentPath, paths);
}
}
/**
* 格式化区域
*
* @param id 区域编号
* @return 格式化后的区域
*/
public static String format(Integer id) {
return format(id, " ");
}
/**
* 格式化区域
*
* 例如说
* 1. id = 静安区上海 上海市 静安区
* 2. id = 上海市上海 上海市
* 3. id = 上海上海
* 4. id = 美国美国
* 当区域在中国时默认不显示中国
*
* @param id 区域编号
* @param separator 分隔符
* @return 格式化后的区域
*/
public static String format(Integer id, String separator) {
// 获得区域
Area area = areas.get(id);
if (area == null) {
return null;
}
// 格式化
StringBuilder sb = new StringBuilder();
for (int i = 0; i < AreaTypeEnum.values().length; i++) { // 避免死循环
sb.insert(0, area.getName());
// 递归父节点
area = area.getParent();
if (area == null
|| ObjectUtils.equalsAny(area.getId(), Area.ID_GLOBAL, Area.ID_CHINA)) { // 跳过父节点为中国的情况
break;
}
sb.insert(0, separator);
}
return sb.toString();
}
/**
* 获取指定类型的区域列表
*
* @param type 区域类型
* @param func 转换函数
* @param <T> 结果类型
* @return 区域列表
*/
public static <T> List<T> getByType(AreaTypeEnum type, Function<Area, T> func) {
return convertList(areas.values(), func, area -> type.getType().equals(area.getType()));
}
/**
* 根据区域编号上级区域类型获取上级区域编号
*
* @param id 区域编号
* @param type 区域类型
* @return 上级区域编号
*/
public static Integer getParentIdByType(Integer id, @NonNull AreaTypeEnum type) {
for (int i = 0; i < Byte.MAX_VALUE; i++) {
Area area = AreaUtils.getArea(id);
if (area == null) {
return null;
}
// 情况一匹配到返回它
if (type.getType().equals(area.getType())) {
return area.getId();
}
// 情况二找到根节点返回空
if (area.getParent() == null || area.getParent().getId() == null) {
return null;
}
// 其它继续向上查找
id = area.getParent().getId();
}
return null;
}
}

View File

@ -0,0 +1,10 @@
package cn.iocoder.yudao.module.haoka.api.liantong.area;
public class TestMain {
public static void main(String[] args) {
Area area = AreaUtils.getArea(0);
System.out.println("----");
}
}

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.haoka.controller.admin.api;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.haoka.api.HaokaAllSuperiorApiService;
import cn.iocoder.yudao.module.haoka.api.ApiLianTongService;
import cn.iocoder.yudao.module.haoka.api.liantong.model.request.*;
import cn.iocoder.yudao.module.haoka.api.liantong.model.response.*;
import io.swagger.v3.oas.annotations.Operation;
@ -19,7 +19,7 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
public class HaokaApiController {
@Resource
private HaokaAllSuperiorApiService haokaAllSuperiorApiService;
private ApiLianTongService haokaAllSuperiorApiService;
@PostMapping("/lian-tong/select-number")
@Operation(summary = "联通选号接口")