diff --git a/java/common/src/main/java/com/cpop/nftmeta/common/wenchang/configuration/LogClientHttpRequestInterceptor.java b/java/common/src/main/java/com/cpop/nftmeta/common/wenchang/configuration/LogClientHttpRequestInterceptor.java index 2647468..0cffb23 100644 --- a/java/common/src/main/java/com/cpop/nftmeta/common/wenchang/configuration/LogClientHttpRequestInterceptor.java +++ b/java/common/src/main/java/com/cpop/nftmeta/common/wenchang/configuration/LogClientHttpRequestInterceptor.java @@ -4,6 +4,7 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpRequest; import org.springframework.http.MediaType; @@ -20,6 +21,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @Component +@Slf4j public class LogClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { @@ -46,7 +48,7 @@ public class LogClientHttpRequestInterceptor implements ClientHttpRequestInterce RestLog restLog = RestLog.builder().costTime(stopWatch.getLastTaskTimeMillis()).headers(request.getHeaders()).method(request.getMethodValue()) .reqUrl(request.getURI().toString()).reqBody(new String(body, StandardCharsets.UTF_8)).resBody(resBody.toString()).resStatus(response.getRawStatusCode()).build(); - System.out.println(restLog.toString()); + log.info("请求信息:{}", restLog.toString()); return response; } diff --git a/java/common/src/main/java/com/cpop/nftmeta/common/wenchang/model/req/WorkRecordReq.java b/java/common/src/main/java/com/cpop/nftmeta/common/wenchang/model/req/WorkRecordReq.java new file mode 100644 index 0000000..5f0eb37 --- /dev/null +++ b/java/common/src/main/java/com/cpop/nftmeta/common/wenchang/model/req/WorkRecordReq.java @@ -0,0 +1,21 @@ +package com.cpop.nftmeta.common.wenchang.model.req; + +import lombok.Data; + +/** + * @Version: 1.0 + * @since: 2024/12/10 + * @author: yxz + */ +@Data +public class WorkRecordReq { + private Integer identity_type; + private String identity_name; + private String identity_num; + private Integer type; + private String name; + private String description; + private String hash; + private Integer hash_type; + private String operation_id; +} diff --git a/java/common/src/main/java/com/cpop/nftmeta/common/wenchang/proxy/AvataProxy.java b/java/common/src/main/java/com/cpop/nftmeta/common/wenchang/proxy/AvataProxy.java index f5836c5..61cfbf2 100644 --- a/java/common/src/main/java/com/cpop/nftmeta/common/wenchang/proxy/AvataProxy.java +++ b/java/common/src/main/java/com/cpop/nftmeta/common/wenchang/proxy/AvataProxy.java @@ -83,4 +83,14 @@ public interface AvataProxy { * @return */ NFTOperateDTO editNFT(EditNftReq editNftReq); + + /** + * 作品存证 + * + * @param workRecordReq + * @return + * @author yxz + * @since 2024/12/10 + */ + NFTOperateDTO workRecord(WorkRecordReq workRecordReq) throws IllegalAccessException; } diff --git a/java/common/src/main/java/com/cpop/nftmeta/common/wenchang/proxy/impl/AvataProxyImpl.java b/java/common/src/main/java/com/cpop/nftmeta/common/wenchang/proxy/impl/AvataProxyImpl.java index 8a98ab7..59201a0 100644 --- a/java/common/src/main/java/com/cpop/nftmeta/common/wenchang/proxy/impl/AvataProxyImpl.java +++ b/java/common/src/main/java/com/cpop/nftmeta/common/wenchang/proxy/impl/AvataProxyImpl.java @@ -18,6 +18,7 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; import javax.annotation.Resource; +import java.lang.reflect.Field; import java.net.URI; import java.util.HashMap; import java.util.Map; @@ -45,6 +46,8 @@ public class AvataProxyImpl implements AvataProxy { private static final String NFT_DESTORY_URL_PRE = "/v1beta1/nft/nfts/"; private static final String NFT_QUERY_URL = "/v1beta1/nft/nfts"; private static final String NFT_QUERY_DETAILS_URL = "/v1beta1/nft/nfts/"; + private static final String RECORDS_URL = "/v1beta1/record/records"; + @Override public CreateAccountDTO createAccount(String name, String operationId) { if (StringUtils.isBlank(name)) { @@ -141,6 +144,52 @@ public class AvataProxyImpl implements AvataProxy { } } + /** + * 数字作品存证 + * + * @param workRecordReq 工作记录要求 + * @return {@link NFTOperateDTO } + * @author yxz + * @since 2024/12/10 + */ + @Override + public NFTOperateDTO workRecord(WorkRecordReq workRecordReq) throws IllegalAccessException { + Map map = new HashMap<>(); + Class clazz = workRecordReq.getClass(); + for (Field field : clazz.getDeclaredFields()) { + field.setAccessible(true); + map.put(field.getName(), field.get(workRecordReq)); + } + Long currentTime = System.currentTimeMillis(); + // 验签 + String signature = AvataUtils.signRequest(RECORDS_URL, null, map, currentTime, wenchangConfig.getApiSecret()); + // 请求体 + HttpHeaders headers = getHttpHeader(signature, currentTime); + headers.setContentType(MediaType.parseMediaType("application/json")); + + HttpEntity httpEntity = new HttpEntity<>(new Gson().toJson(map), headers); + + String url = wenchangConfig.getBaseUrl() + RECORDS_URL; + try { + // 接口调用 + ParameterizedTypeReference> typeReference = new ParameterizedTypeReference>() { + }; + ResponseEntity> responseEntity = restTemplate.exchange(url, HttpMethod.POST, httpEntity, typeReference); + Message message = responseEntity.getBody(); + assert message != null; + NFTOperateDTO nftOperateDTO = message.getData(); + // 成功 + log.info("创建数字作品存证成功:" + nftOperateDTO); + + return nftOperateDTO; + } catch (Exception e) { + // 非200都会抛异常 + // 处理异常 + log.error("非200创建数字作品存证失败:参数为:" + workRecordReq); + e.printStackTrace(); + throw new RuntimeException("创建数字作品存证失败"); + } + } @Override public NFTClassListDTO queryNFTClassList(NFTClassListReq nftClassListReq) { diff --git a/java/nft-admin/src/main/java/com/cpop/nftmeta/admin/tasks/CommonTask.java b/java/nft-admin/src/main/java/com/cpop/nftmeta/admin/tasks/CommonTask.java index 0aab83e..5ef6dba 100644 --- a/java/nft-admin/src/main/java/com/cpop/nftmeta/admin/tasks/CommonTask.java +++ b/java/nft-admin/src/main/java/com/cpop/nftmeta/admin/tasks/CommonTask.java @@ -131,4 +131,16 @@ public class CommonTask { redisHashManager.deleteKey(RedisKey.USER_COUNTS_API_CHATGPT.getKey()); redisHashManager.deleteKey(RedisKey.USER_COUNTS_API_MIDJOURNEY.getKey()); } + + /** + * 更新过期门票状态 + * + * @author yxz + * @since 2024/12/02 + */ + @Scheduled(cron = "0 1 0 * * ?") + public void executeExpiredTicket() { + log.info("=========== 更新过期门票状态 ==========="); + // TODO 查找所有演出时间超过当前时间一天且未核销的门票,并更新其状态为已过期 + } } diff --git a/java/nft-admin/src/main/resources/application-prod.yml b/java/nft-admin/src/main/resources/application-prod.yml index f930f7e..7e133e2 100644 --- a/java/nft-admin/src/main/resources/application-prod.yml +++ b/java/nft-admin/src/main/resources/application-prod.yml @@ -50,16 +50,28 @@ spring: min-idle: 5 youzan: shop-id: 110316359 -# 正式链 +## 正式链 +#nft: +# wenchang: +# base-url: https://apis.avata.bianjie.ai +# chain-id: wenchangchain +# api-key: i2Y2K1o131v5o180O1D8C0L7t9a1P6Vo +# api-secret: j2g2G1u191G5N190R1y8F0h779U1O6qB +# default-class-id: avata888575453de22109e3e97da67e3fe1380fbc41df3fdb3344564bacd2972fca4f +# default-owner: iaa1m7a6dwlydrc0kautfhu7m2ncjgne5n5d0rc8dj +# project-id: 7UkrOsYJEbCd53Kl + +# 六本木链 nft: wenchang: base-url: https://apis.avata.bianjie.ai - chain-id: wenchangchain - api-key: i2Y2K1o131v5o180O1D8C0L7t9a1P6Vo - api-secret: j2g2G1u191G5N190R1y8F0h779U1O6qB + chain-id: wenchang-tianzhou + api-key: e2V4A0t8h2i790Q3y4x2T2k3L63764fw + api-secret: x2j4Y0A8d2z730D3944232D3D6x7t4Q4 default-class-id: avata888575453de22109e3e97da67e3fe1380fbc41df3fdb3344564bacd2972fca4f - default-owner: iaa1m7a6dwlydrc0kautfhu7m2ncjgne5n5d0rc8dj - project-id: 7UkrOsYJEbCd53Kl + default-owner: iaa1pfctqdrl9cae6fk99g8kjn62fegme9zyj3kkr4 + project-id: 42y4b0x882Q770C3 + # 测试链 #nft: # wenchang: diff --git a/java/nft-admin/src/test/java/com/cpop/nftmeta/admin/test/NFTTest.java b/java/nft-admin/src/test/java/com/cpop/nftmeta/admin/test/NFTTest.java index cdba116..5a901b7 100644 --- a/java/nft-admin/src/test/java/com/cpop/nftmeta/admin/test/NFTTest.java +++ b/java/nft-admin/src/test/java/com/cpop/nftmeta/admin/test/NFTTest.java @@ -17,6 +17,7 @@ import com.cpop.nftmeta.common.wenchang.model.dto.NFTDataDTO; import com.cpop.nftmeta.common.wenchang.model.dto.NFTOperateDTO; import com.cpop.nftmeta.common.wenchang.model.req.NFTDestoryReq; import com.cpop.nftmeta.common.wenchang.model.req.NFTPublishReq; +import com.cpop.nftmeta.common.wenchang.model.req.WorkRecordReq; import com.cpop.nftmeta.common.wenchang.proxy.AvataProxy; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; @@ -152,4 +153,39 @@ public class NFTTest { memberNftMapper.updateById(memberNft); } } + + /** + * 数字作品存证 + * + * @author yxz + * @since 2024/12/10 + */ + @Test + public void wordRecord() throws IllegalAccessException { + WorkRecordReq workRecordReq = new WorkRecordReq(); + workRecordReq.setIdentity_type(2); + workRecordReq.setIdentity_name("厦门六本木文化传媒有限公司"); + workRecordReq.setIdentity_num("91350203MA31GKE04B"); + workRecordReq.setType(1); + workRecordReq.setName("汕头超级音雄巨星演唱会门票-内场1080"); + workRecordReq.setDescription("演唱会数字门票"); + String hash = MD5.create().digestHex("https://uthmeta-1300336827.cos.ap-guangzhou.myqcloud.com/1733815941636.jpg"); + workRecordReq.setHash(hash); + workRecordReq.setHash_type(3); + String operationId = UUID.fastUUID().toString(Boolean.FALSE); + log.info("operationId:{}", operationId); + workRecordReq.setOperation_id(operationId); + avataProxy.workRecord(workRecordReq); + } + + /** + * 上链交易结果查询 + * + * @author yxz + * @since 2024/12/10 + */ + @Test + public void queryDealResult() { + avataProxy.queryDealResult("b9c10e3d-1f1b-4077-ac0c-8412e142cbc6"); + } } diff --git a/java/nft-api/src/main/java/com/cpop/nftmeta/api/controller/ZTicketFolderController.java b/java/nft-api/src/main/java/com/cpop/nftmeta/api/controller/ZTicketFolderController.java index fc34746..f27bd59 100644 --- a/java/nft-api/src/main/java/com/cpop/nftmeta/api/controller/ZTicketFolderController.java +++ b/java/nft-api/src/main/java/com/cpop/nftmeta/api/controller/ZTicketFolderController.java @@ -72,6 +72,17 @@ public class ZTicketFolderController { // 门票数据 BeanUtils.copyProperties(e, vo,"audienceName","idNumber"); if (e.getStatus() != 3){ + String qrcode ; + // 票据二维码 用户id和票夹id + if (e.getAudienceName() == null || e.getIdNumber() == null){ + qrcode = "请完善观演人信息"; + }else { + String qrcodeString = userInfo.getUserId() + ":" + e.getFolderId(); + String encrypt = aesUtils.encrypt(qrcodeString); + qrcode = "data:image/png;base64," + ImgUtil.toBase64( + QrCodeUtil.generate(encrypt, 128, 128),"png"); + } + vo.setQrCode(qrcode); // 演出数据 ZShow show = zShowService.getById(e.getShowId()); BeanUtils.copyProperties(show, vo); @@ -85,12 +96,6 @@ public class ZTicketFolderController { // 票档名称 ZTicketTier tier = zTicketTierService.getById(e.getTicketTierId()); vo.setTierName(tier.getTierName()); - // 票据二维码 用户id和票夹id - String qrcodeString = userInfo.getUserId() + ":" + e.getFolderId(); - String encrypt = aesUtils.encrypt(qrcodeString); - String qrcode = "data:image/png;base64," + ImgUtil.toBase64( - QrCodeUtil.generate(encrypt, 128, 128),"png"); - vo.setQrCode(qrcode); } return vo; }).collect(Collectors.toList()); @@ -108,6 +113,17 @@ public class ZTicketFolderController { TicketFolderVO vo = new TicketFolderVO(); // 门票数据 BeanUtils.copyProperties(e, vo,"audienceName","idNumber"); + String qrcode ; + // 票据二维码 用户id和票夹id + if (e.getAudienceName() == null || e.getIdNumber() == null){ + qrcode = "请完善观演人信息"; + } else { + String qrcodeString = userInfo.getUserId() + ":" + e.getFolderId(); + String encrypt = aesUtils.encrypt(qrcodeString); + qrcode = "data:image/png;base64," + ImgUtil.toBase64( + QrCodeUtil.generate(encrypt, 128, 128),"png"); + } + vo.setQrCode(qrcode); // 观演人信息 String audienceName = e.getAudienceName(); String newAudienceName = audienceName.substring(0, audienceName.length() - 1) + "*"; @@ -121,12 +137,6 @@ public class ZTicketFolderController { // 票档名称 ZTicketTier tier = zTicketTierService.getById(e.getTicketTierId()); vo.setTierName(tier.getTierName()); - // 票据二维码 用户id和票夹id - String qrcodeString = userInfo.getUserId() + ":" + e.getFolderId(); - String encrypt = aesUtils.encrypt(qrcodeString); - String qrcode = "data:image/png;base64," + ImgUtil.toBase64( - QrCodeUtil.generate(encrypt, 128, 128),"png"); - vo.setQrCode(qrcode); return vo; }).collect(Collectors.toList()); } diff --git a/java/nft-api/src/main/resources/application-prod.yml b/java/nft-api/src/main/resources/application-prod.yml index 6d06a58..6778695 100644 --- a/java/nft-api/src/main/resources/application-prod.yml +++ b/java/nft-api/src/main/resources/application-prod.yml @@ -40,16 +40,27 @@ spring: youzan: shop-id: 110316359 -# 生产链 +# 数元链 +#nft: +# wenchang: +# base-url: https://apis.avata.bianjie.ai +# chain-id: wenchangchain +# api-key: i2Y2K1o131v5o180O1D8C0L7t9a1P6Vo +# api-secret: j2g2G1u191G5N190R1y8F0h779U1O6qB +# default-class-id: avata888575453de22109e3e97da67e3fe1380fbc41df3fdb3344564bacd2972fca4f +# default-owner: iaa1m7a6dwlydrc0kautfhu7m2ncjgne5n5d0rc8dj +# project-id: 7UkrOsYJEbCd53Kl + +# 六本木链 nft: wenchang: base-url: https://apis.avata.bianjie.ai - chain-id: wenchangchain - api-key: i2Y2K1o131v5o180O1D8C0L7t9a1P6Vo - api-secret: j2g2G1u191G5N190R1y8F0h779U1O6qB + chain-id: wenchang-tianzhou + api-key: e2V4A0t8h2i790Q3y4x2T2k3L63764fw + api-secret: x2j4Y0A8d2z730D3944232D3D6x7t4Q4 default-class-id: avata888575453de22109e3e97da67e3fe1380fbc41df3fdb3344564bacd2972fca4f - default-owner: iaa1m7a6dwlydrc0kautfhu7m2ncjgne5n5d0rc8dj - project-id: 7UkrOsYJEbCd53Kl + default-owner: iaa1pfctqdrl9cae6fk99g8kjn62fegme9zyj3kkr4 + project-id: 42y4b0x882Q770C3 # 测试链 #nft: