├── blokinfo ├── babel.config.js ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── assets │ │ └── logo.png │ ├── main.js │ ├── pages │ │ └── Index.vue │ └── App.vue ├── .gitignore ├── blokinfo.iml ├── README.md ├── vue.config.js └── package.json ├── sdk ├── README.md ├── src │ ├── main │ │ ├── resources │ │ │ ├── application.properties │ │ │ ├── application.yml │ │ │ └── logback-spring.xml │ │ └── java │ │ │ └── cn │ │ │ └── huse │ │ │ └── trace │ │ │ ├── web │ │ │ ├── common │ │ │ │ ├── Constants.java │ │ │ │ ├── auth │ │ │ │ │ ├── jwt │ │ │ │ │ │ ├── exception │ │ │ │ │ │ │ └── JwtParseException.java │ │ │ │ │ │ ├── JwtUser.java │ │ │ │ │ │ ├── JwtConfig.java │ │ │ │ │ │ └── JwtUserTokenUtil.java │ │ │ │ │ └── verify │ │ │ │ │ │ ├── exception │ │ │ │ │ │ └── AuthenticationException.java │ │ │ │ │ │ ├── Authenticator.java │ │ │ │ │ │ └── AuthenticatorImp.java │ │ │ │ ├── QueryResult.java │ │ │ │ ├── ResultCode.java │ │ │ │ ├── ReturnMessage.java │ │ │ │ ├── ReturnMessageMap.java │ │ │ │ ├── PageResult.java │ │ │ │ └── Utils.java │ │ │ ├── entity │ │ │ │ ├── BaseEntity.java │ │ │ │ ├── Transaction.java │ │ │ │ ├── Project.java │ │ │ │ └── User.java │ │ │ ├── dao │ │ │ │ ├── DaoException.java │ │ │ │ ├── UserDao.java │ │ │ │ ├── ProjectDao.java │ │ │ │ ├── TransactionDao.java │ │ │ │ ├── FabricDao.java │ │ │ │ └── BaseDao.java │ │ │ ├── config │ │ │ │ ├── parsetoken │ │ │ │ │ ├── ParseToken.java │ │ │ │ │ └── ParseTokenResolver.java │ │ │ │ ├── BeenConfig.java │ │ │ │ └── SwaggerConfig.java │ │ │ ├── cache │ │ │ │ ├── CacheHelper.java │ │ │ │ └── RedisDao.java │ │ │ ├── service │ │ │ │ ├── UserService.java │ │ │ │ ├── ProjectService.java │ │ │ │ └── TransactionService.java │ │ │ ├── controller │ │ │ │ ├── home │ │ │ │ │ ├── PublicController.java │ │ │ │ │ ├── ProjectController.java │ │ │ │ │ └── UserController.java │ │ │ │ └── blocknet │ │ │ │ │ ├── BlockChainController.java │ │ │ │ │ └── BlockController.java │ │ │ └── response │ │ │ │ └── model │ │ │ │ ├── BlockChainInfoModel.java │ │ │ │ ├── BlockInfoModel.java │ │ │ │ └── EnvelopeModel.java │ │ │ ├── sdk │ │ │ ├── util │ │ │ │ ├── StringUtil.java │ │ │ │ ├── MD5Helper.java │ │ │ │ └── DateUtil.java │ │ │ ├── Functions.java │ │ │ └── trace │ │ │ │ ├── Location.java │ │ │ │ ├── bean │ │ │ │ ├── Orderers.java │ │ │ │ ├── Chaincode.java │ │ │ │ └── Peers.java │ │ │ │ ├── FabricConfig.java │ │ │ │ ├── FabricManager.java │ │ │ │ ├── FabricUser.java │ │ │ │ ├── FabricStore.java │ │ │ │ ├── FabricOrg.java │ │ │ │ └── ChaincodeManager.java │ │ │ └── TraceApplication.java │ └── test │ │ └── java │ │ └── cn │ │ └── huse │ │ └── trace │ │ ├── TraceApplicationTests.java │ │ └── Test.java ├── .gitignore ├── pom.xml ├── mvnw.cmd └── mvnw └── README.md /blokinfo/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /blokinfo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huan-xi/blockchain_trace/HEAD/blokinfo/public/favicon.ico -------------------------------------------------------------------------------- /blokinfo/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huan-xi/blockchain_trace/HEAD/blokinfo/src/assets/logo.png -------------------------------------------------------------------------------- /sdk/README.md: -------------------------------------------------------------------------------- 1 | # 基于Hyperleder Fabric 溯源系统 2 | 3 | go语言链代码 4 | 5 | java SDK 提供api 6 | 7 | 8 | 9 | 1. 搭建网络 10 | 2. 创建链代码 11 | 3. 创建sdk -------------------------------------------------------------------------------- /sdk/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | fabric.path=C:\\Users\\huanxi\\Desktop\\project\\trace\\sdk\\src\\main\\resources\\fabric 2 | upload.path=C:\\Users\\huanxi\\Pictures\\upload\\ 3 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/common/Constants.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.common; 2 | 3 | /** 4 | * @author: huanxi 5 | * @date: 2019/3/26 21:31 6 | */ 7 | public class Constants { 8 | } 9 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/entity/BaseEntity.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.entity; 2 | 3 | /** 4 | * @author: huanxi 5 | * @date: 2019/4/28 13:51 6 | */ 7 | public interface BaseEntity { 8 | String getId(); 9 | } 10 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/dao/DaoException.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.dao; 2 | 3 | /** 4 | * @author: huanxi 5 | * @date: 2019/4/28 11:51 6 | */ 7 | public class DaoException extends Exception { 8 | public DaoException(String e){ 9 | super(e); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /sdk/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | spring: 4 | servlet: 5 | multipart: 6 | max-request-size:10MB 7 | max-file-size=10MB 8 | redis: 9 | host: 120.77.34.74 10 | timeout: 10000 11 | couchdb: 12 | url: http://120.77.34.74:5984/mychannel_funding/ 13 | -------------------------------------------------------------------------------- /blokinfo/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import iView from 'iview' 4 | import 'iview/dist/styles/iview.css' 5 | import 'font-awesome/css/font-awesome.min.css' 6 | Vue.config.productionTip = false 7 | Vue.use(iView) 8 | new Vue({ 9 | render: h => h(App), 10 | }).$mount('#app') 11 | -------------------------------------------------------------------------------- /blokinfo/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /blokinfo/blokinfo.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/dao/UserDao.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.dao; 2 | 3 | import cn.huse.trace.web.entity.User; 4 | import org.springframework.stereotype.Component; 5 | 6 | /** 7 | * @author: huanxi 8 | * @date: 2019/4/27 21:35 9 | */ 10 | @Component 11 | public class UserDao extends BaseDao{ 12 | 13 | } 14 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/common/auth/jwt/exception/JwtParseException.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.common.auth.jwt.exception; 2 | 3 | /** 4 | * @author: huanxi 5 | * @date: 2019-04-15 09:52 6 | */ 7 | public class JwtParseException extends Exception{ 8 | public JwtParseException(String s) { 9 | super(s); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/config/parsetoken/ParseToken.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.config.parsetoken; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * @author: huanxi 7 | * @date: 2019-04-15 23:47 8 | */ 9 | @Target({ElementType.PARAMETER}) 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Documented 12 | public @interface ParseToken { 13 | } 14 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/common/auth/jwt/JwtUser.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.common.auth.jwt; 2 | 3 | /** 4 | * @author: huanxi 5 | * @date: 2019-04-15 09:33 6 | */ 7 | public interface JwtUser { 8 | Object getJwtUserId(); 9 | void setJwtUserId(Object subject); 10 | 11 | void setJwtStatus(Object status); 12 | 13 | Object getJwtStatus(); 14 | } 15 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/common/auth/verify/exception/AuthenticationException.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.common.auth.verify.exception; 2 | 3 | /** 4 | * 权限认证异常 5 | * @author: huanxi 6 | * @date: 2019-04-16 00:31 7 | */ 8 | public class AuthenticationException extends Exception { 9 | public AuthenticationException(String msg) { 10 | super(msg); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/sdk/util/StringUtil.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.sdk.util; 2 | 3 | import org.apache.commons.lang.StringEscapeUtils; 4 | 5 | /** 6 | * @author: huanxi 7 | * @date: 2019/4/27 22:57 8 | */ 9 | public class StringUtil { 10 | public static String formatJsonString(String s) { 11 | return StringEscapeUtils.unescapeJava(s.substring(1, s.length() - 1)); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /sdk/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | /target/ 3 | /logs/ 4 | !.mvn/wrapper/maven-wrapper.jar 5 | 6 | ### STS ### 7 | .apt_generated 8 | .classpath 9 | .factorypath 10 | .project 11 | .settings 12 | .springBeans 13 | .sts4-cache 14 | 15 | ### IntelliJ IDEA ### 16 | .idea 17 | *.iws 18 | *.iml 19 | *.ipr 20 | 21 | ### NetBeans ### 22 | /nbproject/private/ 23 | /nbbuild/ 24 | /dist/ 25 | /nbdist/ 26 | /.nb-gradle/ 27 | /build/ 28 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/sdk/Functions.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.sdk; 2 | 3 | /** 4 | * @author: huanxi 5 | * @date: 2019/3/15 14:16 6 | */ 7 | public class Functions { 8 | public final static String initMilk = "initMilk"; 9 | public final static String readMilk = "readMilk"; 10 | public static String processMilk = "processMilk"; 11 | public static String inspectionMilk = "inspectionMilk"; 12 | } 13 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/common/auth/verify/Authenticator.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.common.auth.verify; 2 | 3 | 4 | import javax.servlet.http.HttpServletRequest; 5 | import javax.servlet.http.HttpServletResponse; 6 | 7 | /** 8 | * @author: huanxi 9 | * @date: 2019-04-16 00:21 10 | */ 11 | public interface Authenticator { 12 | boolean doVerify(HttpServletRequest request, HttpServletResponse response); 13 | } 14 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/config/BeenConfig.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.config; 2 | 3 | import okhttp3.OkHttpClient; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | /** 8 | * @author: huanxi 9 | * @date: 2019/4/28 21:23 10 | */ 11 | @Configuration 12 | public class BeenConfig { 13 | @Bean 14 | OkHttpClient getOkHttpClient() { 15 | return new OkHttpClient(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /blokinfo/README.md: -------------------------------------------------------------------------------- 1 | # blokinfo 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Run your tests 19 | ``` 20 | npm run test 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | npm run lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/common/QueryResult.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.common; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author: huanxi 7 | * @date: 2019/4/29 20:30 8 | */ 9 | @Data 10 | public class QueryResult { 11 | public static int CODE_SUCCESS = 1; 12 | public static int CODE_ERROR = 0; 13 | private int code; 14 | private String data; 15 | private String txId; 16 | 17 | public boolean isSuccess() { 18 | return this.code == CODE_SUCCESS; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /blokinfo/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | devServer: { 3 | open: true, 4 | host: 'localhost', 5 | port: 8080, 6 | https: false, 7 | //以上的ip和端口是我们本机的;下面为需要跨域的 8 | proxy: {//配置跨域 9 | '/api': { 10 | //111.230.251.119 11 | target: 'http://localhost:8888',//这里后台的地址模拟的;应该填写你们真实的后台接口 12 | ws: true, 13 | changOrigin: true,//允许跨域 14 | pathRewrite: { 15 | '^/api': ''//请求的时候使用这个api就可以 16 | } 17 | } 18 | 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /blokinfo/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | IMP溯源系统区块浏览器 9 | 10 | 11 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/entity/Transaction.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * @author: huanxi 9 | * @date: 2019/4/29 12:10 10 | */ 11 | @Data 12 | public class Transaction implements BaseEntity, Serializable { 13 | 14 | public static final String TRANSACTION_FLAG = Transaction.class.getSimpleName(); 15 | private String id; 16 | private float amount; 17 | private String inId; 18 | private String outId; 19 | private long time; 20 | //交易备注 21 | private String desc; 22 | 23 | public Transaction() { 24 | this.time = System.currentTimeMillis(); 25 | } 26 | 27 | @Override 28 | public String getId() { 29 | return this.id; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/common/ResultCode.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.common; 2 | 3 | /** 4 | * @author wuweifeng wrote on 2017/10/23. 5 | */ 6 | public enum ResultCode { 7 | //成功 8 | SUCCESS(200), 9 | //失败 10 | FAIL(400), 11 | //未认证(签名错误) 12 | UNAUTHORIZED(401), 13 | //没有登录 14 | NO_LOGIN(402), 15 | //没有权限 16 | NO_PERMISSION(403), 17 | //接口不存在 18 | NOT_FOUND(404), 19 | //用户状态异常、公司状态异常、产品状态异常 20 | STATE_ERROR(406), 21 | //服务器内部错误 22 | INTERNAL_SERVER_ERROR(500), 23 | //参数错误 24 | PARAMETER_ERROR(10001), 25 | //账号错误 26 | ACCOUNT_ERROR(20001), 27 | //登录失败 28 | LOGIN_FAIL_ERROR(20002); 29 | 30 | 31 | public int code; 32 | 33 | ResultCode(int code) { 34 | this.code = code; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/common/auth/jwt/JwtConfig.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.common.auth.jwt; 2 | 3 | /** 4 | * @author: huanxi 5 | * @date: 2019-04-15 09:38 6 | */ 7 | 8 | public class JwtConfig { 9 | private static long expirationDate=10000000000L; //过期时间 ms 10 | private static String jwtSecret="c13d6445755cddf860b425a6b128ed1c"; //秘钥 11 | 12 | public static long getExpirationDate() { 13 | return expirationDate; 14 | } 15 | 16 | public static void setExpirationDate(long expirationDate) { 17 | JwtConfig.expirationDate = expirationDate; 18 | } 19 | 20 | public static String getJwtSecret() { 21 | return jwtSecret; 22 | } 23 | 24 | public static void setJwtSecret(String jwtSecret) { 25 | JwtConfig.jwtSecret = jwtSecret; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/TraceApplication.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace; 2 | 3 | import cn.huse.trace.web.config.parsetoken.ParseTokenResolver; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.web.method.support.HandlerMethodArgumentResolver; 7 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; 8 | 9 | import java.util.List; 10 | 11 | @SpringBootApplication 12 | public class TraceApplication extends WebMvcConfigurationSupport { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(TraceApplication.class, args); 16 | } 17 | @Override 18 | protected void addArgumentResolvers(List argumentResolvers) { 19 | argumentResolvers.add(new ParseTokenResolver()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/common/auth/verify/AuthenticatorImp.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.common.auth.verify; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | import javax.servlet.http.HttpServletRequest; 6 | import javax.servlet.http.HttpServletResponse; 7 | import java.util.ArrayList; 8 | 9 | /** 10 | * @author: huanxi 11 | * @date: 2019-04-16 00:29 12 | */ 13 | @Service 14 | public class AuthenticatorImp implements Authenticator{ 15 | ArrayList notLogin=new ArrayList<>(); 16 | 17 | public AuthenticatorImp(){ 18 | notLogin.add("/user/token"); 19 | } 20 | 21 | public boolean isLogin(String url) { 22 | //正则匹配 23 | return false; 24 | } 25 | @Override 26 | public boolean doVerify(HttpServletRequest request, HttpServletResponse response) { 27 | System.out.println(request.getRequestURI()); 28 | return true; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/cache/CacheHelper.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.cache; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import javax.annotation.Resource; 6 | 7 | /** 8 | * @author: huanxi 9 | * @date: 2019/4/29 13:30 10 | */ 11 | @Component 12 | public class CacheHelper { 13 | @Resource 14 | RedisDao redisDao; 15 | private final static String KEY_CANAVLE = "key_can_able_"; //缓存是否能用 16 | public final static String VALUEBYTE = "valueByte"; //valueByte 17 | 18 | //检查键是否发生改变 19 | public boolean isKeyAble(String key) { 20 | Object a = redisDao.get(KEY_CANAVLE + key); 21 | return a != null && (boolean) a; 22 | } 23 | 24 | /** 25 | * 设置键发生改变 26 | * 27 | * @param key 28 | * @param change true 为缓存可用 29 | * @return 30 | */ 31 | public boolean setKeyAble(String key, boolean change) { 32 | return redisDao.set(KEY_CANAVLE + key, change); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/common/ReturnMessage.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.common; 2 | 3 | /** 4 | * @author wuweifeng wrote on 2017/10/23. 5 | */ 6 | public class ReturnMessage { 7 | private int code; 8 | private Object msg; 9 | 10 | @Override 11 | public String toString() { 12 | return "ReturnMessage{" + 13 | "code=" + code + 14 | ", data=" + msg + 15 | '}'; 16 | } 17 | 18 | public ReturnMessage(int code, Object msg) { 19 | this.code = code; 20 | this.msg = msg; 21 | } 22 | 23 | public ReturnMessage setCode(ResultCode resultCode) { 24 | this.code = resultCode.code; 25 | return this; 26 | } 27 | 28 | public int getCode() { 29 | return code; 30 | } 31 | 32 | public ReturnMessage setCode(int code) { 33 | this.code = code; 34 | return this; 35 | } 36 | 37 | public Object getMsg() { 38 | return msg; 39 | } 40 | 41 | public void setMsg(Object msg) { 42 | this.msg = msg; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/service/UserService.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.service; 2 | 3 | import cn.huse.trace.web.common.Utils; 4 | import cn.huse.trace.web.dao.DaoException; 5 | import cn.huse.trace.web.dao.UserDao; 6 | import cn.huse.trace.web.entity.User; 7 | import org.springframework.stereotype.Service; 8 | 9 | import javax.annotation.Resource; 10 | 11 | /** 12 | * @author: huanxi 13 | * @date: 2019/4/27 22:25 14 | */ 15 | @Service 16 | public class UserService { 17 | @Resource 18 | UserDao userDao; 19 | 20 | private final String USER_FLAY = "user_"; 21 | 22 | public void addUser(User user) throws DaoException { 23 | user.setStatus(1); 24 | user.setAccount(USER_FLAY + user.getAccount()); 25 | user.setPassword(Utils.passwd(user.getPassword())); 26 | userDao.add(user); 27 | } 28 | 29 | public User getUser(String account) { 30 | // account = USER_FLAY + account; 31 | return userDao.get(account); 32 | } 33 | 34 | public void update(User user) throws DaoException { 35 | userDao.update(user); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/sdk/trace/Location.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.sdk.trace; 2 | 3 | /** 4 | * @author: huanxi 5 | * @date: 2019/3/7 15:54 6 | */ 7 | public class Location { 8 | // private final static String IP = "111.230.251.119"; 9 | private final static String IP = "120.77.34.74"; 10 | private final static String CHANNEL_NAME = "120.77.34.74"; 11 | private static String OrdererLocation = getLocation(7050); 12 | private static String Peer0Location = getLocation(7051); 13 | private static String peerEventHubLocation = getLocation(7053); 14 | private static String getLocation(int port) { 15 | return "grpc://" + IP + ":" + port; 16 | } 17 | 18 | public static String getIP() { 19 | return IP; 20 | } 21 | 22 | public static String getPeer0Location() { 23 | return Peer0Location; 24 | } 25 | 26 | public static String getPeerEventHubLocation() { 27 | return peerEventHubLocation; 28 | } 29 | 30 | public static String getOrdererLocation() { 31 | return OrdererLocation; 32 | } 33 | 34 | public static String getCaLocation() { 35 | return "http://" + IP + ":7054"; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /blokinfo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blokinfo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "axios": "^0.18.0", 12 | "core-js": "^2.6.5", 13 | "echarts": "^4.2.1", 14 | "font-awesome": "^4.7.0", 15 | "iview": "^3.3.3", 16 | "vue": "^2.6.6" 17 | }, 18 | "devDependencies": { 19 | "@vue/cli-plugin-babel": "^3.5.0", 20 | "@vue/cli-plugin-eslint": "^3.5.0", 21 | "@vue/cli-service": "^3.6.0", 22 | "babel-eslint": "^10.0.1", 23 | "eslint": "^5.8.0", 24 | "eslint-plugin-vue": "^5.0.0", 25 | "vue-template-compiler": "^2.5.21" 26 | }, 27 | "eslintConfig": { 28 | "root": true, 29 | "env": { 30 | "node": true 31 | }, 32 | "extends": [ 33 | "plugin:vue/essential", 34 | "eslint:recommended" 35 | ], 36 | "rules": {}, 37 | "parserOptions": { 38 | "parser": "babel-eslint" 39 | } 40 | }, 41 | "postcss": { 42 | "plugins": { 43 | "autoprefixer": {} 44 | } 45 | }, 46 | "browserslist": [ 47 | "> 1%", 48 | "last 2 versions", 49 | "not ie <= 8" 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/common/ReturnMessageMap.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.common; 2 | 3 | import java.io.Serializable; 4 | import java.util.HashMap; 5 | 6 | public class ReturnMessageMap extends HashMap implements Serializable { 7 | private static final long serialVersionUID = -3307660546347616895L; 8 | private final int OK_RESPONSE = 200; 9 | private int status;//状态码 10 | 11 | public ReturnMessageMap(Object msg) { 12 | putOk(); 13 | addMsg(msg); 14 | } 15 | 16 | public int getStatus() { 17 | return status; 18 | } 19 | 20 | public ReturnMessageMap() { 21 | putOk(); 22 | } 23 | 24 | private void putOk() { 25 | this.put("status", this.OK_RESPONSE); 26 | } 27 | 28 | public ReturnMessageMap(int status) { 29 | this.put("status", status); 30 | } 31 | public ReturnMessageMap(int status,String msg) { 32 | this.put("status",status); 33 | this.put("msg",msg); 34 | } 35 | 36 | public void addError(String error) { 37 | this.put("error", error); 38 | } 39 | public void addMsg(Object error) { 40 | this.put("msg", error); 41 | } 42 | 43 | public void setStatus(int status) { 44 | this.status = status; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/common/PageResult.java: -------------------------------------------------------------------------------- 1 | // 2 | // Source code recreated from a .class file by IntelliJ IDEA 3 | // (powered by Fernflower decompiler) 4 | // 5 | 6 | package cn.huse.trace.web.common; 7 | 8 | import java.io.Serializable; 9 | import java.util.List; 10 | 11 | public class PageResult implements Serializable { 12 | private static final long serialVersionUID = 6159543136429894293L; 13 | private long total; 14 | private List rows; 15 | private int currentPage; 16 | 17 | public PageResult(long total, List rows, int currentPage) { 18 | this.total = total; 19 | this.rows = rows; 20 | this.currentPage = currentPage; 21 | } 22 | 23 | public PageResult() { 24 | } 25 | 26 | public long getTotal() { 27 | return this.total; 28 | } 29 | 30 | public void setTotal(long total) { 31 | this.total = total; 32 | } 33 | 34 | public List getRows() { 35 | return this.rows; 36 | } 37 | 38 | public void setRows(List rows) { 39 | this.rows = rows; 40 | } 41 | 42 | public int getCurrentPage() { 43 | return this.currentPage; 44 | } 45 | 46 | public void setCurrentPage(int currentPage) { 47 | this.currentPage = currentPage; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/controller/home/PublicController.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.controller.home; 2 | 3 | import cn.huse.trace.web.common.ReturnMessageMap; 4 | import cn.huse.trace.web.common.Utils; 5 | import io.swagger.annotations.Api; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | import org.springframework.web.multipart.MultipartFile; 11 | 12 | import java.io.File; 13 | 14 | /** 15 | * @author: huanxi 16 | * @date: 2019/3/15 23:26 17 | */ 18 | @RestController 19 | @Api(value = "公开接口", description = "公开接口") 20 | @RequestMapping("/public") 21 | public class PublicController { 22 | @Value("${upload.path}") 23 | String uploadPath; 24 | 25 | @PostMapping("upload") 26 | public ReturnMessageMap upload(MultipartFile image) { 27 | File file = null; 28 | try { 29 | file = Utils.saveFile(image, uploadPath); 30 | } catch (Exception e) { 31 | return new ReturnMessageMap(5013, e.getMessage()); 32 | } 33 | if (file == null) return new ReturnMessageMap(5014, "save file failed"); 34 | return new ReturnMessageMap(file.getName()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/entity/Project.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import io.swagger.annotations.ApiModel; 5 | import io.swagger.annotations.ApiModelProperty; 6 | import lombok.Data; 7 | import lombok.NoArgsConstructor; 8 | 9 | import java.io.Serializable; 10 | 11 | /** 12 | * 众筹项目 13 | * 14 | * @author: huanxi 15 | * @date: 2019/4/28 13:35 16 | */ 17 | @Data 18 | @NoArgsConstructor 19 | @ApiModel("众筹项目") 20 | public class Project implements BaseEntity, Serializable { 21 | public static final int STATUS_PASS = 1; 22 | public static final int STATUS_NOT_PASS = 1; 23 | public static final int STATUS_WAITE = 0; 24 | public static final int STATUS_ALL = -1; 25 | @ApiModelProperty("项目Id") 26 | private String projectId; 27 | @ApiModelProperty("项目描述图片") 28 | private String image; 29 | @ApiModelProperty("发起用户") 30 | private String userId; 31 | @ApiModelProperty("项目标题") 32 | private String title; 33 | @ApiModelProperty("项目描述") 34 | private String desc; 35 | @ApiModelProperty("目标金额") 36 | private float targetAmount; 37 | @ApiModelProperty("状态(0 待审核,1 审核通过,2 审核未通过)") 38 | private int status; 39 | 40 | @Override 41 | @JsonIgnore 42 | public String getId() { 43 | return this.projectId; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/service/ProjectService.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.service; 2 | 3 | import cn.huse.trace.web.common.Utils; 4 | import cn.huse.trace.web.dao.DaoException; 5 | import cn.huse.trace.web.dao.ProjectDao; 6 | import cn.huse.trace.web.entity.Project; 7 | import org.springframework.stereotype.Service; 8 | 9 | import javax.annotation.Resource; 10 | import java.util.List; 11 | 12 | /** 13 | * @author: huanxi 14 | * @date: 2019/4/28 13:44 15 | */ 16 | @Service 17 | public class ProjectService { 18 | @Resource 19 | ProjectDao projectDao; 20 | 21 | private final static String PROJECT_FLAG = "Project_"; 22 | 23 | public void addProject(Project project) throws DaoException { 24 | project.setProjectId(PROJECT_FLAG + project.getUserId() + "_" + Utils.getUUID()); 25 | project.setStatus(Project.STATUS_WAITE); 26 | projectDao.add(project); 27 | } 28 | 29 | public Project getProject(String projectId) { 30 | return projectDao.get(projectId); 31 | } 32 | 33 | public List all(int page, int size,int status) { 34 | return projectDao.all(page, size, status); 35 | } 36 | 37 | public void update(Project project) throws DaoException { 38 | projectDao.update(project); 39 | } 40 | 41 | public List queryByUserId(String userId) { 42 | return projectDao.queryByUserId(userId); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/dao/ProjectDao.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.dao; 2 | 3 | import cn.huse.trace.web.entity.Project; 4 | import org.springframework.stereotype.Component; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author: huanxi 10 | * @date: 2019/4/28 13:44 11 | */ 12 | @Component 13 | public class ProjectDao extends BaseDao { 14 | private static final String PAGE = ",\"limit\": %d,\"skip\": %d"; 15 | private static final String SELECROT_BY_UDER_ID = "{\"selector\": {\"_id\": {\"$regex\": \"^Project_%s_.{0,}$\"}}}"; 16 | private static final String SELECROT_ALL = "{\"selector\": {\"_id\": {\"$regex\": \"^Project_.{0,}$\"}}}"; 17 | 18 | public List all(int page, int size, int status) { 19 | List ts; 20 | String sql = String.format(SELECROT_ALL, size, size * (page - 1)); 21 | ts = query(sql); 22 | if (ts != null && ts.size() > 0) { 23 | List res; 24 | if (status != Project.STATUS_ALL) { 25 | List finalTs = ts; 26 | ts.forEach(t -> { 27 | if (t.getStatus() != status) finalTs.remove(t); 28 | }); 29 | ts = finalTs; 30 | } else return null; 31 | } 32 | return ts; 33 | } 34 | 35 | public List queryByUserId(String userId) { 36 | String sql = String.format(SELECROT_BY_UDER_ID, userId); 37 | return query(sql); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/service/TransactionService.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.service; 2 | 3 | import cn.huse.trace.web.common.Utils; 4 | import cn.huse.trace.web.dao.DaoException; 5 | import cn.huse.trace.web.dao.TransactionDao; 6 | import cn.huse.trace.web.entity.Transaction; 7 | import org.springframework.stereotype.Service; 8 | 9 | import javax.annotation.Resource; 10 | import java.util.List; 11 | 12 | /** 13 | * @author: huanxi 14 | * @date: 2019/4/28 13:44 15 | */ 16 | @Service 17 | public class TransactionService { 18 | @Resource 19 | TransactionDao transactionDao; 20 | 21 | public void addTransaction(Transaction transaction) throws DaoException { 22 | //构造Id 23 | transaction.setId(Transaction.TRANSACTION_FLAG + "_" + transaction.getInId() + "_" + transaction.getOutId() + "_" + Utils.getUUID()); 24 | transactionDao.add(transaction); 25 | } 26 | 27 | public float getBalance(String userId) { 28 | return transactionDao.getBalance(userId); 29 | } 30 | 31 | public List getTransactionByUserId(String userKey) { 32 | return transactionDao.getTransactionByUserId(userKey); 33 | } 34 | 35 | public List getTransactionInByUserId(String userKey) { 36 | return transactionDao.getTransactionInByUserId(userKey); 37 | } 38 | 39 | public List getTransactionOutByUserId(String userKey) { 40 | return transactionDao.getTransactionOutByUserId(userKey); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/response/model/BlockChainInfoModel.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.response.model; 2 | 3 | import io.swagger.annotations.ApiModel; 4 | import io.swagger.annotations.ApiModelProperty; 5 | 6 | /** 7 | * @author: huanxi 8 | * @date: 2019/3/15 13:33 9 | */ 10 | @ApiModel("区块链信息") 11 | public class BlockChainInfoModel { 12 | @ApiModelProperty("区块链区块数量") 13 | private Long height; 14 | @ApiModelProperty("最新区块Hash") 15 | private String currentBlockHash; 16 | @ApiModelProperty("前一区块Hash值") 17 | private String previousBlockHash; 18 | 19 | public BlockChainInfoModel() { 20 | } 21 | 22 | public BlockChainInfoModel(Long height, String currentBlockHash, String previousBlockHash) { 23 | this.height = height; 24 | this.currentBlockHash = currentBlockHash; 25 | this.previousBlockHash = previousBlockHash; 26 | } 27 | 28 | public Long getHeight() { 29 | return height; 30 | } 31 | 32 | public void setHeight(Long height) { 33 | this.height = height; 34 | } 35 | 36 | public String getCurrentBlockHash() { 37 | return currentBlockHash; 38 | } 39 | 40 | public void setCurrentBlockHash(String currentBlockHash) { 41 | this.currentBlockHash = currentBlockHash; 42 | } 43 | 44 | public String getPreviousBlockHash() { 45 | return previousBlockHash; 46 | } 47 | 48 | public void setPreviousBlockHash(String previousBlockHash) { 49 | this.previousBlockHash = previousBlockHash; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 6 | import springfox.documentation.builders.ApiInfoBuilder; 7 | import springfox.documentation.builders.PathSelectors; 8 | import springfox.documentation.builders.RequestHandlerSelectors; 9 | import springfox.documentation.service.ApiInfo; 10 | import springfox.documentation.spi.DocumentationType; 11 | import springfox.documentation.spring.web.plugins.Docket; 12 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 13 | /** 14 | * swagger配置 15 | */ 16 | @Configuration 17 | @EnableSwagger2 18 | public class SwaggerConfig { 19 | @Bean 20 | public Docket createRestApi() { 21 | return new Docket(DocumentationType.SWAGGER_2) 22 | .apiInfo(apiInfo()) 23 | .select() 24 | .apis(RequestHandlerSelectors.basePackage("cn.huse.trace.web.controller")) 25 | .paths(PathSelectors.any()) 26 | .build(); 27 | } 28 | private ApiInfo apiInfo() { 29 | return new ApiInfoBuilder() 30 | .title("基于区块链的溯源系统RESTful APIs") 31 | .description("更多请关注http://www.baidu.com") 32 | .termsOfServiceUrl("http://www.baidu.com") 33 | .contact("huanxi") 34 | .version("1.0") 35 | .build(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/config/parsetoken/ParseTokenResolver.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.config.parsetoken; 2 | 3 | import cn.huse.trace.web.common.auth.jwt.JwtUserTokenUtil; 4 | import cn.huse.trace.web.entity.User; 5 | import org.springframework.core.MethodParameter; 6 | import org.springframework.web.bind.support.WebDataBinderFactory; 7 | import org.springframework.web.context.request.NativeWebRequest; 8 | import org.springframework.web.method.support.HandlerMethodArgumentResolver; 9 | import org.springframework.web.method.support.ModelAndViewContainer; 10 | 11 | /** 12 | * Tokn 解析 13 | * 讲请求头中的token解析成User绑定到controller上 14 | * 15 | * @author: huanxi 16 | * @date: 2019-04-15 23:45 17 | */ 18 | public class ParseTokenResolver implements HandlerMethodArgumentResolver { 19 | @Override 20 | public boolean supportsParameter(MethodParameter methodParameter) { 21 | //如果有ParseToken则使用该解析器 22 | return methodParameter.hasParameterAnnotation(ParseToken.class); 23 | } 24 | 25 | @Override 26 | public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception { 27 | String[] tokens = nativeWebRequest.getHeaderValues("token"); 28 | if (tokens != null && tokens.length > 0) { 29 | String token = tokens[0]; 30 | return JwtUserTokenUtil.getUserFormToken(token, User.class).getJwtUserId(); 31 | } 32 | throw new Exception("token error"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/entity/User.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.entity; 2 | 3 | import cn.huse.trace.web.common.auth.jwt.JwtUser; 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import io.swagger.annotations.ApiModel; 6 | import io.swagger.annotations.ApiModelProperty; 7 | import lombok.Data; 8 | import lombok.NoArgsConstructor; 9 | 10 | import javax.validation.constraints.NotNull; 11 | import java.io.Serializable; 12 | 13 | /** 14 | * @author: huanxi 15 | * @date: 2019/4/27 21:44 16 | */ 17 | 18 | @NoArgsConstructor 19 | @ApiModel("用户") 20 | @Data 21 | public class User implements JwtUser, BaseEntity, Serializable { 22 | @ApiModelProperty("头像地址") 23 | private String headerUrl; 24 | @ApiModelProperty("性别") 25 | private String sex; 26 | @ApiModelProperty("昵称") 27 | private String name; 28 | @NotNull 29 | @ApiModelProperty("账号") 30 | private String account; 31 | @NotNull 32 | @ApiModelProperty("密码") 33 | private String password; 34 | @ApiModelProperty("状态") 35 | private int status; 36 | @Override 37 | @JsonIgnore 38 | public Object getJwtUserId() { 39 | return account; 40 | } 41 | 42 | @Override 43 | public void setJwtUserId(Object subject) { 44 | this.account = (String) subject; 45 | } 46 | 47 | @Override 48 | public void setJwtStatus(Object status) { 49 | this.status = (int) status; 50 | } 51 | 52 | @Override 53 | @JsonIgnore 54 | public Object getJwtStatus() { 55 | return this.status; 56 | } 57 | 58 | @Override 59 | @JsonIgnore 60 | public String getId() { 61 | return this.account; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /sdk/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n 11 | 12 | 13 | 14 | 15 | 16 | 17 | true 18 | 19 | ./logs/logFile.%d{yyyy-MM-dd}.log 20 | 21 | 30 22 | 3GB 23 | 24 | 25 | 26 | %d{yyyy-MM-dd HH:mm:ss} -%msg%n 27 | 28 | 29 | %-4relative [%thread] %-5level %logger{35} - %msg%n 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 43 | 44 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/common/Utils.java: -------------------------------------------------------------------------------- 1 | // 2 | // Source code recreated from a .class file by IntelliJ IDEA 3 | // (powered by Fernflower decompiler) 4 | // 5 | 6 | package cn.huse.trace.web.common; 7 | 8 | import org.apache.commons.codec.digest.DigestUtils; 9 | import org.springframework.web.multipart.MultipartFile; 10 | 11 | import java.io.*; 12 | 13 | public class Utils { 14 | 15 | 16 | public static File saveFile(MultipartFile image, String uploadPath) throws Exception { 17 | if (image != null && image.isEmpty()) throw new Exception("image is empty"); 18 | File file = new File(uploadPath + Utils.getUUID() + "." + image.getContentType().split("/")[1]); 19 | OutputStream os = null; 20 | InputStream in = null; 21 | try { 22 | os = new FileOutputStream(file); 23 | in = image.getInputStream(); 24 | int i;//从输入流读取一定数量的字节,返回 0 到 255 范围内的 int 型字节值 25 | while ((i = in.read()) != -1) { 26 | os.write(i); 27 | } 28 | } catch (Exception e) { 29 | e.printStackTrace(); 30 | } finally { 31 | try { 32 | in.close(); 33 | os.close(); 34 | } catch (IOException e) { 35 | e.printStackTrace(); 36 | } 37 | } 38 | return file; 39 | } 40 | 41 | public static String passwd(String s) { 42 | return DigestUtils.md5Hex("imp" + s); 43 | } 44 | 45 | public static String generateToken() { 46 | return DigestUtils.md5Hex("imp" + System.currentTimeMillis() + Math.random()); 47 | } 48 | 49 | public static String getUUID() { 50 | return DigestUtils.md5Hex("imp" + System.currentTimeMillis() + Math.random()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/sdk/trace/bean/Orderers.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.sdk.trace.bean; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Fabric创建的orderer信息,涵盖单机和集群两种方案 8 | * 9 | * @author 杨毅 10 | * 11 | * @date 2017年10月18日 - 下午1:56:48 12 | * @email abericyang@gmail.com 13 | */ 14 | public class Orderers { 15 | 16 | /** orderer 排序服务器所在根域名 */ 17 | private String ordererDomainName; // anti-moth.com 18 | /** orderer 排序服务器集合 */ 19 | private List orderers; 20 | 21 | public Orderers() { 22 | orderers = new ArrayList<>(); 23 | } 24 | 25 | public String getOrdererDomainName() { 26 | return ordererDomainName; 27 | } 28 | 29 | public void setOrdererDomainName(String ordererDomainName) { 30 | this.ordererDomainName = ordererDomainName; 31 | } 32 | 33 | /** 新增排序服务器 */ 34 | public void addOrderer(String name, String location) { 35 | orderers.add(new Orderer(name, location)); 36 | } 37 | 38 | /** 获取排序服务器集合 */ 39 | public List get() { 40 | return orderers; 41 | } 42 | 43 | /** 44 | * 排序服务器对象 45 | * 46 | * @author 杨毅 47 | * 48 | * @date 2017年10月18日 - 下午2:06:22 49 | * @email abericyang@gmail.com 50 | */ 51 | public class Orderer { 52 | 53 | /** orderer 排序服务器的域名 */ 54 | private String ordererName; 55 | /** orderer 排序服务器的访问地址 */ 56 | private String ordererLocation; 57 | 58 | public Orderer(String ordererName, String ordererLocation) { 59 | super(); 60 | this.ordererName = ordererName; 61 | this.ordererLocation = ordererLocation; 62 | } 63 | 64 | public String getOrdererName() { 65 | return ordererName; 66 | } 67 | 68 | public void setOrdererName(String ordererName) { 69 | this.ordererName = ordererName; 70 | } 71 | 72 | public String getOrdererLocation() { 73 | return ordererLocation; 74 | } 75 | 76 | public void setOrdererLocation(String ordererLocation) { 77 | this.ordererLocation = ordererLocation; 78 | } 79 | 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/sdk/trace/bean/Chaincode.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.sdk.trace.bean; 2 | 3 | /** 4 | * Fabric创建的chaincode信息,涵盖所属channel等信息 5 | * 6 | * @author 杨毅 7 | * 8 | * @date 2017年10月18日 - 下午2:07:42 9 | * @email abericyang@gmail.com 10 | */ 11 | public class Chaincode { 12 | 13 | /** 当前将要访问的智能合约所属频道名称 */ 14 | private String channelName; // ffetest 15 | /** 智能合约名称 */ 16 | private String chaincodeName; // ffetestcc 17 | /** 智能合约安装路径 */ 18 | private String chaincodePath; // github.com/hyperledger/fabric/xxx/chaincode/go/example/test 19 | /** 智能合约版本号 */ 20 | private String chaincodeVersion; // 1.0 21 | /** 临时变量来控制等待部署和调用的时间,以完成在发出之前的事件。当SDK能够接收来自于此的事件时,这个问题就会被删除 */ 22 | private int transactionWaitTime = 100000; 23 | /** 临时变量来控制等待部署和调用的时间,以完成在发出之前的事件。当SDK能够接收来自于此的事件时,这个问题就会被删除 */ 24 | private int deployWatiTime = 120000; 25 | 26 | public String getChannelName() { 27 | return channelName; 28 | } 29 | 30 | public void setChannelName(String channelName) { 31 | this.channelName = channelName; 32 | } 33 | 34 | public String getChaincodeName() { 35 | return chaincodeName; 36 | } 37 | 38 | public void setChaincodeName(String chaincodeName) { 39 | this.chaincodeName = chaincodeName; 40 | } 41 | 42 | public String getChaincodePath() { 43 | return chaincodePath; 44 | } 45 | 46 | public void setChaincodePath(String chaincodePath) { 47 | this.chaincodePath = chaincodePath; 48 | } 49 | 50 | public String getChaincodeVersion() { 51 | return chaincodeVersion; 52 | } 53 | 54 | public void setChaincodeVersion(String chaincodeVersion) { 55 | this.chaincodeVersion = chaincodeVersion; 56 | } 57 | 58 | public int getTransactionWaitTime() { 59 | return transactionWaitTime; 60 | } 61 | 62 | public void setTransactionWaitTime(int invokeWatiTime) { 63 | this.transactionWaitTime = invokeWatiTime; 64 | } 65 | 66 | public int getDeployWaitTime() { 67 | return deployWatiTime; 68 | } 69 | 70 | public void setDeployWaitTime(int deployWaitTime) { 71 | this.deployWatiTime = deployWaitTime; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /sdk/src/test/java/cn/huse/trace/TraceApplicationTests.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace; 2 | 3 | import cn.huse.trace.web.dao.DaoException; 4 | import cn.huse.trace.web.dao.ProjectDao; 5 | import cn.huse.trace.web.dao.TransactionDao; 6 | import cn.huse.trace.web.entity.User; 7 | import cn.huse.trace.web.service.UserService; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | import org.springframework.test.context.junit4.SpringRunner; 12 | 13 | import javax.annotation.Resource; 14 | 15 | @RunWith(SpringRunner.class) 16 | @SpringBootTest 17 | public class TraceApplicationTests { 18 | 19 | @Resource 20 | UserService userService; 21 | @Resource 22 | ProjectDao baseDao; 23 | @Resource 24 | TransactionDao transactionDao; 25 | 26 | @Test 27 | public void queryTransaction(){ 28 | // List a = transactionDao.getTransactionByUserId("user_1234ddd"); 29 | System.out.println(transactionDao.getBalance("user_1234ddd")); 30 | } 31 | 32 | @Test 33 | public void query() { 34 | baseDao.deleteAll(); 35 | // Project b = baseDao.getValueBytes("project_1234_1ccd02df63e99f7ded297451c347adb2"); 36 | // System.out.println(b.toString()); 37 | // List a = baseDao.queryByUserId("1234"); 38 | // List a = baseDao.all(1, 12); 39 | } 40 | 41 | @Test 42 | public void addUser() { 43 | userService.getUser("44c0eeda178ee76730f5a399546a26ff"); 44 | } 45 | 46 | @Test 47 | public void contextLoads() { 48 | User user = new User(); 49 | user.setAccount("44c0eeda178ee767w0f5a399546a262f"); 50 | user.setName("user1"); 51 | user.setPassword("123"); 52 | user.setSex("男"); 53 | try { 54 | userService.addUser(user); 55 | } catch (DaoException e) { 56 | e.printStackTrace(); 57 | } 58 | /* if (userService.getUser(user.getAccount())!=null){ 59 | System.out.println("已存在"); 60 | return; 61 | }*/ 62 | // userService.add(user); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/controller/blocknet/BlockChainController.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.controller.blocknet; 2 | 3 | import cn.huse.trace.sdk.trace.ChaincodeManager; 4 | import cn.huse.trace.sdk.trace.FabricManager; 5 | import cn.huse.trace.web.response.model.BlockChainInfoModel; 6 | import io.swagger.annotations.Api; 7 | import io.swagger.annotations.ApiOperation; 8 | import org.apache.commons.codec.binary.Hex; 9 | import org.hyperledger.fabric.sdk.BlockchainInfo; 10 | import org.hyperledger.fabric.sdk.exception.InvalidArgumentException; 11 | import org.hyperledger.fabric.sdk.exception.ProposalException; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.web.bind.annotation.GetMapping; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | import java.util.Objects; 18 | 19 | /** 20 | * @author: huanxi 21 | * @date: 2019/3/15 11:31 22 | */ 23 | 24 | @RequestMapping("/blockchain") 25 | @RestController 26 | @Api(value = "区块链信息", description = "区块链信息查询接口") 27 | public class BlockChainController { 28 | ChaincodeManager fabricManager; 29 | private BlockchainInfo getBlockchainInfo() { 30 | try { 31 | return fabricManager.getChannelInstant().queryBlockchainInfo(); 32 | } catch (ProposalException | InvalidArgumentException e) { 33 | e.printStackTrace(); 34 | } 35 | return null; 36 | } 37 | 38 | @ApiOperation("查询最新区块hash值") 39 | @GetMapping("/currenthash") 40 | public String getCurrentHash() { 41 | byte[] b = Objects.requireNonNull(getBlockchainInfo()).getCurrentBlockHash(); 42 | return Hex.encodeHexString(b); 43 | } 44 | 45 | @ApiOperation("查询区块链信息") 46 | @GetMapping("/info") 47 | public BlockChainInfoModel getInfo() { 48 | BlockchainInfo b = getBlockchainInfo(); 49 | assert b != null; 50 | return new BlockChainInfoModel(b.getHeight(), Hex.encodeHexString(b.getCurrentBlockHash()), Hex.encodeHexString(b.getPreviousBlockHash())); 51 | } 52 | 53 | @Autowired 54 | public void setFabricManager(ChaincodeManager fabricManager) { 55 | this.fabricManager = FabricManager.getInstance().getManager(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /sdk/src/test/java/cn/huse/trace/Test.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace; 2 | 3 | 4 | import com.alibaba.fastjson.JSON; 5 | import com.alibaba.fastjson.JSONArray; 6 | import com.alibaba.fastjson.JSONObject; 7 | import okhttp3.*; 8 | import org.hyperledger.fabric.sdk.exception.CryptoException; 9 | import org.hyperledger.fabric.sdk.exception.InvalidArgumentException; 10 | import org.hyperledger.fabric.sdk.exception.ProposalException; 11 | import org.hyperledger.fabric.sdk.exception.TransactionException; 12 | 13 | import java.io.IOException; 14 | import java.security.NoSuchAlgorithmException; 15 | import java.security.NoSuchProviderException; 16 | import java.security.spec.InvalidKeySpecException; 17 | 18 | /** 19 | * @author: huanxi 20 | * @date: 2019/3/6 23:54 21 | */ 22 | public class Test { 23 | public static final MediaType JSON_HEADER = MediaType.parse("application/json; charset=utf-8"); 24 | 25 | @org.junit.Test 26 | public void testQuery() { 27 | String url = "http://120.77.34.74:5984/mychannel_funding/_find"; 28 | OkHttpClient okHttpClient = new OkHttpClient(); 29 | String pattern = "project_1234"; 30 | String selector = "{\"selector\": {\"_id\": {\"$regex\": \"%s\"}}}"; 31 | String sql = String.format(selector, pattern); 32 | RequestBody body = RequestBody.create(JSON_HEADER, sql); 33 | final Request request = new Request.Builder() 34 | .url(url).post(body) 35 | .build(); 36 | final Call call = okHttpClient.newCall(request); 37 | Response response = null; 38 | try { 39 | response = call.execute(); 40 | } catch (IOException e) { 41 | e.printStackTrace(); 42 | } 43 | assert response != null; 44 | JSONObject res = null; 45 | try { 46 | res = JSON.parseObject(response.body().string()); 47 | } catch (IOException e) { 48 | e.printStackTrace(); 49 | } 50 | JSONArray t = (JSONArray) res.get("docs"); 51 | assert t.size() > 0; 52 | String id = ((JSONObject) t.get(0)).getString("_id"); 53 | System.out.println(id); 54 | } 55 | 56 | @org.junit.Test 57 | public void test() throws InvalidArgumentException, NoSuchAlgorithmException, IOException, TransactionException, NoSuchProviderException, CryptoException, InvalidKeySpecException, ProposalException { 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/response/model/BlockInfoModel.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.response.model; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import io.swagger.annotations.ApiModel; 5 | import io.swagger.annotations.ApiModelProperty; 6 | import org.apache.commons.codec.binary.Hex; 7 | import org.hyperledger.fabric.sdk.BlockInfo; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * @author: huanxi 14 | * @date: 2019/3/15 13:33 15 | */ 16 | @ApiModel("区块信息") 17 | public class BlockInfoModel { 18 | @ApiModelProperty("区块号") 19 | private Long blockNumber; 20 | @ApiModelProperty("区块数据Hash") 21 | private String dataHash; 22 | @ApiModelProperty("前一区块Hash") 23 | private String previousHash; 24 | @ApiModelProperty("大小") 25 | private int size; 26 | @ApiModelProperty("封装信息") 27 | private List envelopes = new ArrayList(); 28 | 29 | public BlockInfoModel(BlockInfo blockInfo) { 30 | this.blockNumber = blockInfo.getBlockNumber(); 31 | this.dataHash = Hex.encodeHexString(blockInfo.getDataHash()); 32 | this.previousHash = Hex.encodeHexString(blockInfo.getPreviousHash()); 33 | //处理分装信息 34 | blockInfo.getEnvelopeInfos().forEach(envelopeInfo -> { 35 | envelopes.add(new EnvelopeModel(envelopeInfo)); 36 | }); 37 | this.size = JSON.toJSONString(this).getBytes().length*1024; 38 | } 39 | 40 | public BlockInfoModel() { 41 | } 42 | 43 | public int getSize() { 44 | return size; 45 | } 46 | 47 | public void setSize(int size) { 48 | this.size = size; 49 | } 50 | 51 | public List getEnvelopes() { 52 | return envelopes; 53 | } 54 | 55 | public void setEnvelopes(List envelopes) { 56 | this.envelopes = envelopes; 57 | } 58 | 59 | public Long getBlockNumber() { 60 | return blockNumber; 61 | } 62 | 63 | public void setBlockNumber(Long blockNumber) { 64 | this.blockNumber = blockNumber; 65 | } 66 | 67 | public String getDataHash() { 68 | return dataHash; 69 | } 70 | 71 | public void setDataHash(String dataHash) { 72 | this.dataHash = dataHash; 73 | } 74 | 75 | public String getPreviousHash() { 76 | return previousHash; 77 | } 78 | 79 | public void setPreviousHash(String previousHash) { 80 | this.previousHash = previousHash; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/controller/home/ProjectController.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.controller.home; 2 | 3 | import cn.huse.trace.web.common.ReturnMessageMap; 4 | import cn.huse.trace.web.config.parsetoken.ParseToken; 5 | import cn.huse.trace.web.dao.DaoException; 6 | import cn.huse.trace.web.entity.Project; 7 | import cn.huse.trace.web.service.ProjectService; 8 | import io.swagger.annotations.Api; 9 | import io.swagger.annotations.ApiOperation; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import javax.annotation.Resource; 13 | 14 | /** 15 | * @author: huanxi 16 | * @date: 2019/4/28 15:52 17 | */ 18 | @RestController 19 | @Api(value = "项目接口", description = "项目接口") 20 | @RequestMapping("project") 21 | public class ProjectController { 22 | 23 | @Resource 24 | private ProjectService projectService; 25 | 26 | /* @GetMapping("all/pass") 27 | @ApiOperation("获取所有已经审核通过的项目") 28 | public ReturnMessageMap getAllPassProject(int page, int size) { 29 | if (page > 0 && size > 0) return new ReturnMessageMap(projectService.all(page, size)); 30 | return null; 31 | } 32 | 33 | @GetMapping("all/nopass") 34 | @ApiOperation("获取所有已经审核通过的项目") 35 | public ReturnMessageMap getAllNoPassProject(int page, int size) { 36 | if (page > 0 && size > 0) return new ReturnMessageMap(projectService.all(page, size,Project.STATUS_NOT_PASS)); 37 | return null; 38 | }*/ 39 | 40 | @GetMapping("all") 41 | @ApiOperation("获取所有已经审核通过的项目") 42 | public ReturnMessageMap getAllProject(int page, int size) { 43 | if (page > 0 && size > 0) return new ReturnMessageMap(projectService.all(page, size, -1)); 44 | return new ReturnMessageMap(4010, "page or size must be gt 0"); 45 | } 46 | 47 | @PostMapping 48 | @ApiOperation("发布众筹项目") 49 | public ReturnMessageMap addProject(Project project, @ParseToken String userId) { 50 | project.setUserId(userId); 51 | try { 52 | projectService.addProject(project); 53 | } catch (DaoException e) { 54 | return new ReturnMessageMap(5010, "add project failed!"); 55 | } 56 | return new ReturnMessageMap("public successfully!"); 57 | } 58 | 59 | @PutMapping 60 | @ApiOperation("更新众筹项目") 61 | public ReturnMessageMap updateProject(Project project, @ParseToken String userId) { 62 | try { 63 | projectService.update(project); 64 | } catch (DaoException e) { 65 | return new ReturnMessageMap(5014, e.getMessage()); 66 | } 67 | return new ReturnMessageMap("public successfully!"); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/sdk/util/MD5Helper.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.sdk.util; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.security.MessageDigest; 5 | import java.security.NoSuchAlgorithmException; 6 | 7 | /** 8 | * @author Aberic 9 | * @Email abericyang@gmail.com 10 | * @date 2017年5月4日 上午9:28:05 11 | */ 12 | public class MD5Helper { 13 | 14 | private static MD5Helper instance; 15 | 16 | public static MD5Helper obtain() { 17 | if (null == instance) { 18 | synchronized (MD5Helper.class) { 19 | if (null == instance) { 20 | instance = new MD5Helper(); 21 | } 22 | } 23 | } 24 | return instance; 25 | } 26 | 27 | private MD5Helper() { 28 | } 29 | 30 | /** 31 | * 将字符串md5散列值24位 32 | * 33 | * @param content 34 | * 散列值内容 35 | * @return 散列值结果 36 | */ 37 | public String md524(String content) { 38 | String str = getMD5(content); 39 | return str.substring(6, 30); 40 | } 41 | 42 | /** 43 | * 将字符串md5散列值24位 44 | * 45 | * @param content 46 | * 散列值内容 47 | * @return 散列值结果 48 | */ 49 | public String md524(int content) { 50 | return md524(String.valueOf(content)); 51 | } 52 | 53 | /** 54 | * 将字符串md5散列值32位 55 | * 56 | * @param content 57 | * 散列值内容 58 | * @return 散列值结果 59 | */ 60 | public String md532(String content) { 61 | return getMD5(content); 62 | } 63 | 64 | /** 65 | * 将整型md5散列值32位 66 | * 67 | * @param content 68 | * 散列值内容 69 | * @return 散列值结果 70 | */ 71 | public String md532(int content) { 72 | return md532(String.valueOf(content)); 73 | } 74 | 75 | /** 76 | * 将字符串md5散列值 77 | * 78 | * @param content 79 | * 散列值内容 80 | * @return 散列值结果 81 | */ 82 | private String getMD5(String content) { 83 | try { 84 | MessageDigest mDigest = MessageDigest.getInstance("MD5"); 85 | mDigest.update(content.getBytes("UTF-8")); 86 | return this.getHashString(mDigest); 87 | } catch (NoSuchAlgorithmException var3) { 88 | var3.printStackTrace(); 89 | return null; 90 | } catch (UnsupportedEncodingException e) { 91 | e.printStackTrace(); 92 | return null; 93 | } 94 | } 95 | 96 | private String getHashString(MessageDigest digest) { 97 | StringBuilder builder = new StringBuilder(); 98 | byte[] var3 = digest.digest(); 99 | int var4 = var3.length; 100 | 101 | for (int var5 = 0; var5 < var4; ++var5) { 102 | byte b = var3[var5]; 103 | builder.append(Integer.toHexString(b >> 4 & 15)); 104 | builder.append(Integer.toHexString(b & 15)); 105 | } 106 | return builder.toString(); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/dao/TransactionDao.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.dao; 2 | 3 | import cn.huse.trace.web.cache.CacheHelper; 4 | import cn.huse.trace.web.cache.RedisDao; 5 | import cn.huse.trace.web.entity.Transaction; 6 | import org.springframework.stereotype.Component; 7 | 8 | import javax.annotation.Resource; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * @author: huanxi 14 | * @date: 2019/4/29 12:14 15 | */ 16 | @Component 17 | public class TransactionDao extends BaseDao { 18 | private static final String SELECTOR_BY_USER_ID = "{\"selector\": {\"_id\": {\"$regex\": \"^Transaction_.{0,}%s.{0,}$\"}}}"; 19 | private static final String KEY_USER_RANSACTION = "user_transaction"; 20 | @Resource 21 | RedisDao redisDao; 22 | @Resource 23 | CacheHelper cacheHelper; 24 | 25 | //获取所有我的交易 26 | public List getTransactionByUserId(String userKey) { 27 | List a = null; 28 | if (cacheHelper.isKeyAble(KEY_USER_RANSACTION + userKey)) { 29 | //读取缓存 30 | a = redisDao.lGet(KEY_USER_RANSACTION + userKey, 0, -1); 31 | if (a != null && a.size() > 0) a = (List) a.get(0); 32 | } 33 | if (a == null) { 34 | String sql = String.format(SELECTOR_BY_USER_ID, userKey); 35 | a = query(sql); 36 | redisDao.lSet(KEY_USER_RANSACTION + userKey, a); 37 | cacheHelper.setKeyAble(KEY_USER_RANSACTION + userKey, true); 38 | } 39 | return a; 40 | } 41 | 42 | //获取所有转给我的 43 | public List getTransactionInByUserId(String userKey) { 44 | List in = new ArrayList<>(); 45 | List a = getTransactionByUserId(userKey); 46 | a.forEach(transaction -> { 47 | if (transaction.getInId().equals(userKey)) 48 | in.add(transaction); 49 | }); 50 | return in; 51 | } 52 | 53 | //获取所有我转出的交易 54 | public List getTransactionOutByUserId(String userKey) { 55 | List out = new ArrayList<>(); 56 | List a = getTransactionByUserId(userKey); 57 | a.forEach(transaction -> { 58 | if (transaction.getOutId().equals(userKey)) 59 | out.add(transaction); 60 | }); 61 | return out; 62 | } 63 | 64 | public float getBalance(String userId) { 65 | final float[] balance = {0}; 66 | List a = getTransactionByUserId(userId); 67 | a.forEach(transaction -> { 68 | if (transaction.getInId().equals(userId)) 69 | balance[0] -= transaction.getAmount(); 70 | else if (transaction.getOutId().equals(userId)) 71 | balance[0] += transaction.getAmount(); 72 | }); 73 | return balance[0]; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 基于HyberLedger fabric 的区块链溯源系统 2 | 3 | 4 | 5 | ## 部署节点说明 6 | 7 | > 由于机器,和ip有限以及集群部署的复杂度,将采用单机部署,为节约资源采用docker容器代替虚拟机,每个端口即是一个docker容器服务。 8 | 9 | 10 | 11 | 节点端口 12 | 13 | - orderder :7050 14 | - 商家组织 15 | - 节点0: 16 | - 发布 7051 17 | - 监听 7053 18 | - 数据库 5984 19 | - 节点1: 20 | - 发布 10051 21 | - 监听 10053 22 | - 数据库 7984 23 | 24 | ### 区块操作 25 | 26 | > 区块操作即是通过sdk 发布提案(公钥证书,私钥签名),通知Orderer节点准备投票 27 | > 28 | > 各节点收到区块信息-> 验证 -> 提交 (即投票) 29 | 30 | - 添加奶粉信息 31 | - 加工奶粉 32 | - 检测奶粉 33 | - 删除奶粉 34 | - 修改奶粉数据 35 | 36 | ### 流程演示 37 | 38 | 1. 区块链信息 39 | 40 | >总共23 个区块,可看到最新区块hash和前一区块hash 41 | 42 | ![1552751522097](C:\Users\huanxi\AppData\Roaming\Typora\typora-user-images\1552751522097.png) 43 | 44 | 2. 添加奶粉 45 | 46 | > 添加一个所有者为haunxi,id为2 ,重量为500g的奶粉,可看到交易id 47 | 48 | ![1552751693290](C:\Users\huanxi\AppData\Roaming\Typora\typora-user-images\1552751693290.png) 49 | 50 | **组织1的peer0 日志** 51 | 52 | ![1552751817529](C:\Users\huanxi\AppData\Roaming\Typora\typora-user-images\1552751817529.png) 53 | 54 | **组织1的peer1 日志** 55 | 56 | ![1552751901138](C:\Users\huanxi\AppData\Roaming\Typora\typora-user-images\1552751901138.png) 57 | 58 | **组织2的peer0 日志** 59 | 60 | ![1552751953623](C:\Users\huanxi\AppData\Roaming\Typora\typora-user-images\1552751953623.png) 61 | 62 | **组织2的peer1日志** 63 | 64 | ![1552751936685](C:\Users\huanxi\AppData\Roaming\Typora\typora-user-images\1552751936685.png) 65 | 66 | > 可以看到4个节点都收到了区块,并验证,然后在提交 67 | 68 | **查看数据库情况** 69 | 70 | > 可以看到4个数据库都插入了相同的数据 71 | 72 | ![1552752113637](C:\Users\huanxi\AppData\Roaming\Typora\typora-user-images\1552752113637.png) 73 | 74 | ![1552752129842](C:\Users\huanxi\AppData\Roaming\Typora\typora-user-images\1552752129842.png) 75 | 76 | ![1552752144453](C:\Users\huanxi\AppData\Roaming\Typora\typora-user-images\1552752144453.png) 77 | 78 | ![1552752163204](C:\Users\huanxi\AppData\Roaming\Typora\typora-user-images\1552752163204.png) 79 | 80 | **再看区块链信息** 81 | 82 | > 对比上面可以看到多了一个区块,并且现在的previoushash等于上一次的currenthash 83 | 84 | ![1552752253958](C:\Users\huanxi\AppData\Roaming\Typora\typora-user-images\1552752253958.png) 85 | 86 | **查看23 区块最新信息** 87 | 88 | > 可以看到该区块有组织一创建,能看到组织1的公钥,ws是该区块的所有写操作,可以看到该区块对key为2的值写了后面的值(区块链采用的couchdb,键值对数据库) 89 | 90 | ![1552752376946](C:\Users\huanxi\AppData\Roaming\Typora\typora-user-images\1552752376946.png) 91 | 92 | **对刚才的奶粉加工** 93 | 94 | > ​ 和添加是同样的流程,不再截图 95 | 96 | ![1552752684445](C:\Users\huanxi\AppData\Roaming\Typora\typora-user-images\1552752684445.png) 97 | 98 | > 看新增的24个区块,可以看到ws中对2 写了新的process_info 99 | 100 | ![1552752816957](C:\Users\huanxi\AppData\Roaming\Typora\typora-user-images\1552752816957.png) 101 | 102 | 103 | 104 | **检测和加工一样就不再演示了** 105 | 106 | 107 | 108 | ### 结束语 109 | 110 | > ​ 通过流程可以看到,我们将组织对数据库的操作写在区块链中。由于区块链具有不可修改性,有区块链维护的数据库具有公开,不可修改性。可以遍历整个区块链得到一个couchdb数据库。 -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/dao/FabricDao.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.dao; 2 | 3 | import cn.huse.trace.sdk.trace.ChaincodeManager; 4 | import cn.huse.trace.sdk.trace.FabricManager; 5 | import cn.huse.trace.web.common.QueryResult; 6 | import com.alibaba.fastjson.JSON; 7 | import org.hyperledger.fabric.sdk.exception.CryptoException; 8 | import org.hyperledger.fabric.sdk.exception.InvalidArgumentException; 9 | import org.hyperledger.fabric.sdk.exception.ProposalException; 10 | import org.hyperledger.fabric.sdk.exception.TransactionException; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.io.IOException; 15 | import java.security.NoSuchAlgorithmException; 16 | import java.security.NoSuchProviderException; 17 | import java.security.spec.InvalidKeySpecException; 18 | import java.util.Map; 19 | import java.util.concurrent.ExecutionException; 20 | import java.util.concurrent.TimeoutException; 21 | 22 | /** 23 | * @author: huanxi 24 | * @date: 2019/4/27 21:47 25 | */ 26 | @Component 27 | public class FabricDao { 28 | ChaincodeManager fabricManager; 29 | 30 | public QueryResult set(String key, Object object) { 31 | try { 32 | return fabricManager.invoke("set", new String[]{key, JSON.toJSONString(object)}); 33 | } catch (InvalidArgumentException | ProposalException | InterruptedException | ExecutionException | TimeoutException | NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException | CryptoException | TransactionException | IOException e) { 34 | e.printStackTrace(); 35 | } 36 | return null; 37 | } 38 | 39 | public QueryResult update(String key, Object object) { 40 | try { 41 | return fabricManager.invoke("update", new String[]{key, JSON.toJSONString(object)}); 42 | } catch (InvalidArgumentException | ProposalException | InterruptedException | ExecutionException | TimeoutException | NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException | CryptoException | TransactionException | IOException e) { 43 | e.printStackTrace(); 44 | } 45 | return null; 46 | } 47 | 48 | public String get(String key) { 49 | try { 50 | Map a = fabricManager.query("get", new String[]{key}); 51 | if (a.get("code").equals("error")) return null; 52 | return a.get("data"); 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | } 56 | return null; 57 | } 58 | 59 | public QueryResult delete(String key) { 60 | try { 61 | return fabricManager.invoke("delete", new String[]{key}); 62 | } catch (InvalidArgumentException | ProposalException | InterruptedException | ExecutionException | TimeoutException | NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException | CryptoException | TransactionException | IOException e) { 63 | e.printStackTrace(); 64 | } 65 | return null; 66 | } 67 | 68 | @Autowired 69 | public void setFabricManager(ChaincodeManager fabricManager) { 70 | this.fabricManager = FabricManager.getInstance().getManager(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /blokinfo/src/pages/Index.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 46 | 105 | 106 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/sdk/trace/FabricConfig.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.sdk.trace; 2 | 3 | import java.io.File; 4 | import java.util.Objects; 5 | 6 | import cn.huse.trace.sdk.trace.bean.Chaincode; 7 | import cn.huse.trace.sdk.trace.bean.Orderers; 8 | import cn.huse.trace.sdk.trace.bean.Peers; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | public class FabricConfig { 13 | 14 | private static Logger log = LoggerFactory.getLogger(FabricConfig.class); 15 | 16 | /** 节点服务器对象 */ 17 | private Peers peers; 18 | /** 排序服务器对象 */ 19 | private Orderers orderers; 20 | /** 智能合约对象 */ 21 | private Chaincode chaincode; 22 | /** channel-artifacts所在路径:默认channel-artifacts所在路径/xxx/WEB-INF/classes/fabric/channel-artifacts/ */ 23 | private String channelArtifactsPath; 24 | /** crypto-config所在路径:默认crypto-config所在路径/xxx/WEB-INF/classes/fabric/crypto-config/ */ 25 | private String cryptoConfigPath; 26 | private boolean openCATLS=true; 27 | private boolean registerEvent = false; 28 | private String directory; 29 | 30 | public FabricConfig() { 31 | // 默认channel-artifacts所在路径 /xxx/WEB-INF/classes/fabric/channel-artifacts/ 32 | channelArtifactsPath = getChannlePath() + "/channel-artifacts/"; 33 | // 默认crypto-config所在路径 /xxx/WEB-INF/classes/fabric/crypto-config/ 34 | cryptoConfigPath = getChannlePath() + "/crypto-config/"; 35 | } 36 | 37 | /** 38 | * 默认fabric配置路径 39 | * 40 | * @return D:/installSoft/apache-tomcat-9.0.0.M21-02/webapps/xxx/WEB-INF/classes/fabric/channel-artifacts/ 41 | */ 42 | private String getChannlePath() { 43 | /*String directorys = Objects.requireNonNull(FabricConfig.class.getClassLoader().getResource("fabric")).getFile(); 44 | log.debug("directorys = " + directorys); 45 | File directory = new File(directorys); 46 | log.debug("directory = " + directory.getPath());*/ 47 | return "classpath:fabric/"; 48 | // return "src/main/resources/fabric/channel-artifacts/"; 49 | } 50 | 51 | public Peers getPeers() { 52 | return peers; 53 | } 54 | 55 | public void setPeers(Peers peers) { 56 | this.peers = peers; 57 | } 58 | 59 | public Orderers getOrderers() { 60 | return orderers; 61 | } 62 | 63 | public void setOrderers(Orderers orderers) { 64 | this.orderers = orderers; 65 | } 66 | 67 | public Chaincode getChaincode() { 68 | return chaincode; 69 | } 70 | 71 | public void setChaincode(Chaincode chaincode) { 72 | this.chaincode = chaincode; 73 | } 74 | 75 | public String getChannelArtifactsPath() { 76 | return channelArtifactsPath; 77 | } 78 | 79 | public void setChannelArtifactsPath(String channelArtifactsPath) { 80 | this.channelArtifactsPath = channelArtifactsPath; 81 | } 82 | 83 | public String getCryptoConfigPath() { 84 | return cryptoConfigPath; 85 | } 86 | 87 | public void setCryptoConfigPath(String cryptoConfigPath) { 88 | this.cryptoConfigPath = cryptoConfigPath; 89 | } 90 | 91 | public boolean isRegisterEvent() { 92 | return registerEvent; 93 | } 94 | 95 | /** 96 | * 设置是否开启TLS 97 | * 98 | * @param openCATLS 99 | * 是否 100 | */ 101 | public void openCATLS(boolean openCATLS) { 102 | this.openCATLS = openCATLS; 103 | } 104 | 105 | /** 106 | * 获取是否开启TLS 107 | * 108 | * @return 是否 109 | */ 110 | public boolean openCATLS() { 111 | return openCATLS; 112 | } 113 | 114 | public void setRegisterEvent(boolean registerEvent) { 115 | this.registerEvent = registerEvent; 116 | } 117 | 118 | public String getDirectory() { 119 | return this.directory; 120 | } 121 | 122 | public void setDirectory(String directory) { 123 | this.directory = directory; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/controller/blocknet/BlockController.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.controller.blocknet; 2 | 3 | import cn.huse.trace.sdk.trace.ChaincodeManager; 4 | import cn.huse.trace.sdk.trace.FabricManager; 5 | import cn.huse.trace.web.common.PageResult; 6 | import cn.huse.trace.web.common.ReturnMessage; 7 | import cn.huse.trace.web.response.model.BlockInfoModel; 8 | import io.swagger.annotations.Api; 9 | import io.swagger.annotations.ApiOperation; 10 | import io.swagger.annotations.ApiParam; 11 | import org.apache.commons.codec.binary.Hex; 12 | import org.hyperledger.fabric.sdk.exception.InvalidArgumentException; 13 | import org.hyperledger.fabric.sdk.exception.ProposalException; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.web.bind.annotation.GetMapping; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RestController; 18 | 19 | import java.io.IOException; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | /** 24 | * @author: huanxi 25 | * @date: 2019/3/15 12:26 26 | */ 27 | @RestController 28 | @RequestMapping("/block") 29 | @Api(value = "区块信息", description = "区块信息查询接口") 30 | public class BlockController { 31 | ChaincodeManager fabricManager; 32 | 33 | @GetMapping("/blocks") 34 | @ApiOperation("获取区块") 35 | public ReturnMessage getBlocks(@ApiParam("页数") int page, @ApiParam("每页大小") int size) { 36 | if (size <= 0) return null; 37 | if (size > 10) { 38 | return new ReturnMessage(3000, "once fetch to many infos"); 39 | } else { 40 | long total = 0; 41 | int totalPage; 42 | List blockInfoModels = new ArrayList<>(); 43 | try { 44 | total = fabricManager.getChannelInstant().queryBlockchainInfo().getHeight(); 45 | totalPage = (int) (total / size); 46 | } catch (ProposalException | InvalidArgumentException e) { 47 | e.printStackTrace(); 48 | return new ReturnMessage(100, "query blockchain error!"); 49 | } 50 | //最后一个 51 | int j=0; 52 | for (long i = total-(page-1)*size-1; j < size; j++,i--) { 53 | if (i < 0) break; 54 | BlockInfoModel b; 55 | try { 56 | b = fabricManager.queryBlockByNumber(i); 57 | } catch (InvalidArgumentException | ProposalException | IOException e) { 58 | e.printStackTrace(); 59 | return new ReturnMessage(12, "query block error!"); 60 | } 61 | blockInfoModels.add(b); 62 | } 63 | return new ReturnMessage(1, new PageResult(total, blockInfoModels, page)); 64 | } 65 | } 66 | 67 | @GetMapping("/query/hash") 68 | @ApiOperation("根据Hash值获取区块信息") 69 | public BlockInfoModel queryByHash(@ApiParam("区块号") String hash) { 70 | try { 71 | return fabricManager.queryBlockByHash(Hex.decodeHex(hash)); 72 | } catch (Exception e) { 73 | e.printStackTrace(); 74 | } 75 | return null; 76 | } 77 | 78 | 79 | @GetMapping("/query/no") 80 | @ApiOperation("根据区块号查询区块,区块号不能超过区块链区块长度") 81 | public BlockInfoModel queryByNo(@ApiParam("区块号") long no) { 82 | try { 83 | return fabricManager.queryBlockByNumber(no); 84 | 85 | } catch (InvalidArgumentException | ProposalException | IOException e) { 86 | e.printStackTrace(); 87 | } 88 | return null; 89 | } 90 | 91 | @GetMapping("/query/txid") 92 | @ApiOperation("根据交易id查询区块") 93 | public BlockInfoModel query(@ApiParam("交易id") String txid) { 94 | try { 95 | return fabricManager.queryBlockByTransactionID(txid); 96 | } catch (Exception e) { 97 | e.printStackTrace(); 98 | } 99 | return null; 100 | } 101 | 102 | @Autowired 103 | public void setFabricManager(ChaincodeManager fabricManager) { 104 | this.fabricManager = FabricManager.getInstance().getManager(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /sdk/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.1.3.RELEASE 9 | 10 | 11 | cn.huse 12 | trace 13 | 0.0.1-SNAPSHOT 14 | trace 15 | Demo project for Spring Boot 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | com.squareup.okhttp3 24 | okhttp 25 | 3.10.0 26 | 27 | 28 | 29 | org.projectlombok 30 | lombok 31 | 1.18.6 32 | 33 | 34 | commons-lang 35 | commons-lang 36 | 2.6 37 | 38 | 39 | com.alibaba 40 | fastjson 41 | 1.2.57 42 | 43 | 44 | io.jsonwebtoken 45 | jjwt 46 | 0.9.0 47 | 48 | 49 | 50 | io.springfox 51 | springfox-swagger2 52 | 2.2.2 53 | 54 | 55 | io.springfox 56 | springfox-swagger-ui 57 | 2.2.2 58 | 59 | 60 | 61 | com.google.guava 62 | guava 63 | 26.0-jre 64 | 65 | 66 | 67 | com.github.xiaoymin 68 | swagger-bootstrap-ui 69 | 1.7 70 | 71 | 72 | com.alibaba 73 | fastjson 74 | 1.2.49 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-starter-web 79 | 80 | 81 | org.hyperledger.fabric-sdk-java 82 | fabric-sdk-java 83 | 1.4.0 84 | 85 | 86 | org.springframework.boot 87 | spring-boot-devtools 88 | runtime 89 | 90 | 91 | org.springframework.boot 92 | spring-boot-starter-test 93 | test 94 | 95 | 96 | org.springframework.boot 97 | spring-boot-starter-data-redis 98 | 99 | 100 | 101 | 102 | 103 | 104 | org.springframework.boot 105 | spring-boot-maven-plugin 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/sdk/trace/bean/Peers.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.sdk.trace.bean; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * Fabric创建的peer信息,包含有cli、org、ca、couchdb等节点服务器关联启动服务信息集合 8 | * 9 | * @author 杨毅 10 | * 11 | * @date 2017年10月18日 - 下午1:49:03 12 | * @email abericyang@gmail.com 13 | */ 14 | public class Peers { 15 | 16 | /** 当前指定的组织名称 */ 17 | private String orgName; // Org1 18 | /** 当前指定的组织名称 */ 19 | private String orgMSPID; // Org1MSP 20 | /** 当前指定的组织所在根域名 */ 21 | private String orgDomainName; // org1.example.com 22 | /** orderer 排序服务器集合 */ 23 | private List peers; 24 | 25 | public Peers() { 26 | peers = new ArrayList<>(); 27 | } 28 | 29 | public String getOrgName() { 30 | return orgName; 31 | } 32 | 33 | public void setOrgName(String orgName) { 34 | this.orgName = orgName; 35 | } 36 | 37 | public String getOrgMSPID() { 38 | return orgMSPID; 39 | } 40 | 41 | public void setOrgMSPID(String orgMSPID) { 42 | this.orgMSPID = orgMSPID; 43 | } 44 | 45 | public String getOrgDomainName() { 46 | return orgDomainName; 47 | } 48 | 49 | public void setOrgDomainName(String orgDomainName) { 50 | this.orgDomainName = orgDomainName; 51 | } 52 | 53 | /** 新增排序服务器 */ 54 | public void addPeer(String peerName, String peerEventHubName, String peerLocation, String peerEventHubLocation, String caLocation) { 55 | peers.add(new Peer(peerName, peerEventHubName, peerLocation, peerEventHubLocation, caLocation)); 56 | } 57 | 58 | /** 获取排序服务器集合 */ 59 | public List get() { 60 | return peers; 61 | } 62 | 63 | /** 64 | * 节点服务器对象 65 | * 66 | * @author 杨毅 67 | * 68 | * @date 2017年11月11日 - 下午6:56:14 69 | * @email abericyang@gmail.com 70 | */ 71 | public class Peer { 72 | 73 | /** 当前指定的组织节点域名 */ 74 | private String peerName; // peer0.org1.example.com 75 | /** 当前指定的组织节点事件域名 */ 76 | private String peerEventHubName; // peer0.org1.example.com 77 | /** 当前指定的组织节点访问地址 */ 78 | private String peerLocation; // grpc://110.131.116.21:7051 79 | /** 当前指定的组织节点事件监听访问地址 */ 80 | private String peerEventHubLocation; // grpc://110.131.116.21:7053 81 | /** 当前指定的组织节点ca访问地址 */ 82 | private String caLocation; // http://110.131.116.21:7054 83 | /** 当前peer是否增加Event事件处理 */ 84 | private boolean addEventHub = false; 85 | 86 | public Peer(String peerName, String peerEventHubName, String peerLocation, String peerEventHubLocation, String caLocation) { 87 | this.peerName = peerName; 88 | this.peerEventHubName = peerEventHubName; 89 | this.peerLocation = peerLocation; 90 | this.peerEventHubLocation = peerEventHubLocation; 91 | this.caLocation = caLocation; 92 | } 93 | 94 | public Peer(String peerName, String peerEventHubName, String peerLocation, String peerEventHubLocation, String caLocation, boolean isEventListener) { 95 | this.peerName = peerName; 96 | this.peerEventHubName = peerEventHubName; 97 | this.peerLocation = peerLocation; 98 | this.peerEventHubLocation = peerEventHubLocation; 99 | this.caLocation = caLocation; 100 | this.addEventHub = isEventListener; 101 | } 102 | 103 | public String getPeerName() { 104 | return peerName; 105 | } 106 | 107 | public void setPeerName(String peerName) { 108 | this.peerName = peerName; 109 | } 110 | 111 | public String getPeerEventHubName() { 112 | return peerEventHubName; 113 | } 114 | 115 | public void setPeerEventHubName(String peerEventHubName) { 116 | this.peerEventHubName = peerEventHubName; 117 | } 118 | 119 | public String getPeerLocation() { 120 | return peerLocation; 121 | } 122 | 123 | public void setPeerLocation(String peerLocation) { 124 | this.peerLocation = peerLocation; 125 | } 126 | 127 | public String getPeerEventHubLocation() { 128 | return peerEventHubLocation; 129 | } 130 | 131 | public void setPeerEventHubLocation(String eventHubLocation) { 132 | this.peerEventHubLocation = eventHubLocation; 133 | } 134 | 135 | public String getCaLocation() { 136 | return caLocation; 137 | } 138 | 139 | public void setCaLocation(String caLocation) { 140 | this.caLocation = caLocation; 141 | } 142 | 143 | public boolean isAddEventHub() { 144 | return addEventHub; 145 | } 146 | 147 | public void addEventHub(boolean addEventHub) { 148 | this.addEventHub = addEventHub; 149 | } 150 | 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/common/auth/jwt/JwtUserTokenUtil.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.common.auth.jwt; 2 | 3 | import cn.huse.trace.web.common.auth.jwt.exception.JwtParseException; 4 | import io.jsonwebtoken.Claims; 5 | import io.jsonwebtoken.Jwts; 6 | import io.jsonwebtoken.SignatureAlgorithm; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.io.Serializable; 10 | import java.util.Date; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | 15 | /** 16 | * JWT工具类 17 | * 18 | */ 19 | @Component 20 | public class JwtUserTokenUtil implements Serializable { 21 | 22 | /** 23 | * 从数据声明生成令牌 24 | * 25 | * @param claims 数据声明 26 | * @return 令牌 27 | */ 28 | private static String generateToken(Map claims) { 29 | Date expirationDate = new Date(System.currentTimeMillis() + JwtConfig.getExpirationDate()); 30 | return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, JwtConfig.getJwtSecret()).compact(); 31 | } 32 | 33 | /** 34 | * 从令牌中获取数据声明 35 | * 36 | * @param token 令牌 37 | * @return 数据声明 38 | */ 39 | public static Claims getClaimsFromToken(String token) { 40 | Claims claims; 41 | try { 42 | claims = Jwts.parser().setSigningKey(JwtConfig.getJwtSecret()).parseClaimsJws(token).getBody(); 43 | } catch (Exception e) { 44 | claims = null; 45 | } 46 | return claims; 47 | } 48 | 49 | /** 50 | * 生成令牌 51 | * 52 | * @return 令牌 53 | */ 54 | public static String generateToken(JwtUser user) { 55 | Map claims = new HashMap<>(2); 56 | claims.put("sub", user.getJwtUserId()); 57 | claims.put("status", user.getJwtStatus()); 58 | claims.put("created", new Date()); 59 | return generateToken(claims); 60 | } 61 | 62 | /** 63 | * 从令牌中获取用户名 64 | * 65 | * @param token 令牌 66 | * @return 用户名 67 | */ 68 | public static String getUserIdFromToken(String token) { 69 | String username; 70 | try { 71 | Claims claims = getClaimsFromToken(token); 72 | username = claims.getSubject(); 73 | } catch (Exception e) { 74 | username = null; 75 | } 76 | return username; 77 | } 78 | 79 | /** 80 | * 获取jwtUser 对象 81 | * @param token 82 | * @return 83 | * @throws JwtParseException 84 | */ 85 | public static JwtUser getUserFormToken(String token,Class u) throws JwtParseException { 86 | JwtUser user= null; 87 | try { 88 | user = (JwtUser) u.newInstance(); 89 | } catch (InstantiationException | IllegalAccessException e) { 90 | e.printStackTrace(); 91 | } 92 | try { 93 | Claims claims = getClaimsFromToken(token); 94 | user.setJwtStatus(claims.get("status")); 95 | user.setJwtUserId(claims.getSubject()); 96 | }catch (Exception e){ 97 | //抓到空指针异常,解析失败 98 | e.printStackTrace(); 99 | throw new JwtParseException("parse this token error!"); 100 | } 101 | 102 | return user; 103 | } 104 | 105 | /** 106 | * 判断令牌是否过期 107 | * 108 | * @param token 令牌 109 | * @return 是否过期 110 | */ 111 | public static Boolean isTokenExpired(String token) { 112 | try { 113 | Claims claims = getClaimsFromToken(token); 114 | Date expiration = claims.getExpiration(); 115 | return expiration.before(new Date()); 116 | } catch (Exception e) { 117 | return false; 118 | } 119 | } 120 | 121 | /** 122 | * 刷新令牌 123 | * 124 | * @param token 原令牌 125 | * @return 新令牌 126 | */ 127 | public String refreshToken(String token) { 128 | String refreshedToken; 129 | try { 130 | Claims claims = getClaimsFromToken(token); 131 | claims.put("created", new Date()); 132 | refreshedToken = generateToken(claims); 133 | } catch (Exception e) { 134 | refreshedToken = null; 135 | } 136 | return refreshedToken; 137 | } 138 | 139 | /** 140 | * 验证令牌 141 | * 142 | * @param token 令牌 143 | * @return 是否有效 144 | */ 145 | public static Boolean validateToken(String token, JwtUser user) { 146 | String username = getUserIdFromToken(token); 147 | return (username.equals(user.getJwtUserId()) && !isTokenExpired(token)); 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/dao/BaseDao.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.dao; 2 | 3 | import cn.huse.trace.sdk.util.StringUtil; 4 | import cn.huse.trace.web.cache.CacheHelper; 5 | import cn.huse.trace.web.cache.RedisDao; 6 | import cn.huse.trace.web.common.QueryResult; 7 | import cn.huse.trace.web.entity.BaseEntity; 8 | import com.alibaba.fastjson.JSON; 9 | import com.alibaba.fastjson.JSONArray; 10 | import com.alibaba.fastjson.JSONObject; 11 | import okhttp3.*; 12 | import org.apache.commons.lang.StringUtils; 13 | import org.springframework.beans.factory.annotation.Value; 14 | 15 | import javax.annotation.Resource; 16 | import java.lang.reflect.ParameterizedType; 17 | import java.lang.reflect.Type; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | /** 22 | * 缓存优化 23 | * 未做更新操作,去缓存中读取 24 | * 做更新,存缓存 25 | * 26 | * @author: huanxi 27 | * @date: 2019/4/28 13:46 28 | */ 29 | public class BaseDao { 30 | @Resource 31 | FabricDao baseDao; 32 | @Value("${couchdb.url}") 33 | protected String url; 34 | @Resource 35 | OkHttpClient okHttpClient; 36 | @Resource 37 | CacheHelper cacheHelper; 38 | @Resource 39 | RedisDao redisDao; 40 | 41 | protected static final MediaType JSON_HEADER = MediaType.parse("application/json; charset=utf-8"); 42 | 43 | public boolean add(T t) throws DaoException { 44 | QueryResult a = baseDao.set(t.getId(), JSON.toJSONString(t)); 45 | if (!a.isSuccess()) throw new DaoException(a.getData()); 46 | return true; 47 | } 48 | 49 | public T get(String id) { 50 | String a = baseDao.get(id); 51 | if (a == null) return null; 52 | T t = null; 53 | try { 54 | ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass(); 55 | Type clazz = type.getActualTypeArguments()[0]; 56 | t = JSON.parseObject(StringUtil.formatJsonString(a), clazz); 57 | } catch (Exception e) { 58 | e.printStackTrace(); 59 | } 60 | return (T) t; 61 | } 62 | 63 | public List query(String sql) { 64 | System.out.println("执行sql语句:" + sql); 65 | List ts = new ArrayList<>(); 66 | RequestBody body = RequestBody.create(JSON_HEADER, sql); 67 | final Request request = new Request.Builder() 68 | .url(url + "_find").post(body) 69 | .build(); 70 | final Call call = okHttpClient.newCall(request); 71 | Response response = null; 72 | try { 73 | response = call.execute(); 74 | assert response != null; 75 | JSONObject res = null; 76 | res = JSON.parseObject(response.body().string()); 77 | JSONArray t = (JSONArray) res.get("docs"); 78 | assert t.size() > 0; 79 | t.forEach(idObject -> { 80 | String id = ((JSONObject) idObject).getString("_id"); 81 | // baseDao.delete(id); 82 | if (!StringUtils.isEmpty(id)) { 83 | T t1 = getValueBytes(id); 84 | if (t1 != null) ts.add(t1); 85 | } 86 | }); 87 | } catch (Exception e) { 88 | return null; 89 | } 90 | return ts; 91 | } 92 | 93 | public boolean update(T t) throws DaoException { 94 | QueryResult a = baseDao.update(t.getId(), JSON.toJSONString(t)); 95 | if (!a.isSuccess()) throw new DaoException(a.getData()); 96 | return true; 97 | } 98 | 99 | public void deleteAll() { 100 | query("{\n" + 101 | " \"selector\": {\n" + 102 | " \"_id\": {\n" + 103 | " \"$gt\": \"\"\n" + 104 | " }\n" + 105 | " }\n" + 106 | "}"); 107 | } 108 | 109 | public T getValueBytes(String id) { 110 | T t = null; 111 | if (cacheHelper.isKeyAble(CacheHelper.VALUEBYTE + id)) { 112 | t = (T) redisDao.get(CacheHelper.VALUEBYTE + id); 113 | } 114 | if (t == null) { 115 | final Request request = new Request.Builder() 116 | .url(url + id + "/valueBytes").get() 117 | .build(); 118 | final Call call = okHttpClient.newCall(request); 119 | Response response = null; 120 | try { 121 | response = call.execute(); 122 | String res = response.body().string(); 123 | assert res != null; 124 | ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass(); 125 | Type clazz = type.getActualTypeArguments()[0]; 126 | t = JSON.parseObject(StringUtil.formatJsonString(res), clazz); 127 | if (t != null) { 128 | redisDao.set(CacheHelper.VALUEBYTE + id, t); 129 | cacheHelper.setKeyAble(CacheHelper.VALUEBYTE + id, true); 130 | } 131 | } catch (Exception e) { 132 | e.printStackTrace(); 133 | return null; 134 | } 135 | } 136 | return t; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/sdk/trace/FabricManager.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.sdk.trace; 2 | 3 | /** 4 | * @author: huanxi 5 | * @date: 2019/3/7 15:37 6 | */ 7 | 8 | import cn.huse.trace.sdk.trace.bean.Chaincode; 9 | import cn.huse.trace.sdk.trace.bean.Orderers; 10 | import cn.huse.trace.sdk.trace.bean.Peers; 11 | import org.hyperledger.fabric.sdk.exception.CryptoException; 12 | import org.hyperledger.fabric.sdk.exception.InvalidArgumentException; 13 | import org.hyperledger.fabric.sdk.exception.TransactionException; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | 17 | import java.io.IOException; 18 | import java.security.NoSuchAlgorithmException; 19 | import java.security.NoSuchProviderException; 20 | import java.security.spec.InvalidKeySpecException; 21 | 22 | public class FabricManager { 23 | 24 | private static final String CHAINCODE_NAME = "funding"; 25 | private static final String CHANNEL_NAME = "mychannel"; 26 | private final FabricConfig config; 27 | private ChaincodeManager manager; 28 | 29 | private static FabricManager instance = null; 30 | private static Logger log = LoggerFactory.getLogger(FabricManager.class); 31 | private String directory; 32 | 33 | public static FabricManager obtain(FabricConfig config) 34 | throws CryptoException, InvalidArgumentException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, TransactionException, IOException { 35 | if (null == instance) { 36 | synchronized (FabricManager.class) { 37 | if (null == instance) { 38 | instance = new FabricManager(config); 39 | } 40 | } 41 | } 42 | return instance; 43 | } 44 | 45 | public static FabricManager getInstance() { 46 | return instance; 47 | } 48 | 49 | private FabricManager(FabricConfig config) 50 | throws CryptoException, InvalidArgumentException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, TransactionException, IOException { 51 | this.config=config; 52 | manager = new ChaincodeManager("Admin", getConfig()); 53 | } 54 | 55 | /** 56 | * 获取节点服务器管理器 57 | * 58 | * @return 节点服务器管理器 59 | */ 60 | public ChaincodeManager getManager() { 61 | return manager; 62 | } 63 | 64 | /** 65 | * 根据节点作用类型获取节点服务器配置 66 | *

67 | * 服务器作用类型(1、执行;2、查询) 68 | * 69 | * @return 节点服务器配置 70 | */ 71 | private FabricConfig getConfig() { 72 | FabricConfig config = new FabricConfig(); 73 | config.setOrderers(getOrderers()); 74 | config.setPeers(getPeers()); 75 | config.setChaincode(getChaincode(CHANNEL_NAME,CHAINCODE_NAME , "github.com/chaincode/chaincode_example02/go/", "1.0")); 76 | config.setChannelArtifactsPath(getChannleArtifactsPath()); 77 | config.setCryptoConfigPath(getCryptoConfigPath()); 78 | return config; 79 | } 80 | 81 | private Orderers getOrderers() { 82 | Orderers orderer = new Orderers(); 83 | orderer.setOrdererDomainName("example.com"); 84 | orderer.addOrderer("orderer.example.com", Location.getOrdererLocation()); 85 | return orderer; 86 | } 87 | 88 | /** 89 | * 获取节点服务器集 90 | * 91 | * @return 节点服务器集 92 | */ 93 | private Peers getPeers() { 94 | Peers peers = new Peers(); 95 | peers.setOrgName("org1"); 96 | peers.setOrgMSPID("Org1MSP"); 97 | peers.setOrgDomainName("org1.example.com"); 98 | peers.addPeer("peer0.org1.example.com", "peer0.org1.example.com", Location.getPeer0Location(), Location.getPeerEventHubLocation(), Location.getCaLocation()); 99 | return peers; 100 | } 101 | 102 | /** 103 | * 获取智能合约 104 | * 105 | * @param channelName 频道名称 106 | * @param chaincodeName 智能合约名称 107 | * @param chaincodePath 智能合约路径 108 | * @param chaincodeVersion 智能合约版本 109 | * @return 智能合约 110 | */ 111 | private Chaincode getChaincode(String channelName, String chaincodeName, String chaincodePath, String chaincodeVersion) { 112 | Chaincode chaincode = new Chaincode(); 113 | chaincode.setChannelName(channelName); 114 | chaincode.setChaincodeName(chaincodeName); 115 | chaincode.setChaincodePath(chaincodePath); 116 | chaincode.setChaincodeVersion(chaincodeVersion); 117 | chaincode.setDeployWaitTime(100000); 118 | chaincode.setTransactionWaitTime(100000); 119 | return chaincode; 120 | } 121 | 122 | /** 123 | * 获取channel-artifacts配置路径 124 | * 125 | * @return /WEB-INF/classes/fabric/channel-artifacts/ 126 | */ 127 | private String getChannleArtifactsPath() { 128 | return getDirectory() + "/channel-artifacts/"; 129 | } 130 | 131 | private String getDirectory() { 132 | return config.getDirectory(); 133 | } 134 | 135 | /** 136 | * 获取crypto-config配置路径 137 | * 138 | * @return /WEB-INF/classes/fabric/crypto-config/ 139 | */ 140 | private String getCryptoConfigPath() { 141 | return getDirectory() + "/crypto-config/"; 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/response/model/EnvelopeModel.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.response.model; 2 | 3 | import cn.huse.trace.sdk.trace.ChaincodeManager; 4 | import com.google.protobuf.InvalidProtocolBufferException; 5 | import io.swagger.annotations.ApiModelProperty; 6 | import org.hyperledger.fabric.protos.ledger.rwset.kvrwset.KvRwset; 7 | import org.hyperledger.fabric.sdk.BlockInfo; 8 | import org.hyperledger.fabric.sdk.TxReadWriteSetInfo; 9 | 10 | import java.io.UnsupportedEncodingException; 11 | import java.util.Date; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | /** 16 | * @author: huanxi 17 | * @date: 2019/3/16 0:07 18 | */ 19 | public class EnvelopeModel { 20 | @ApiModelProperty("信封类型") 21 | private BlockInfo.EnvelopeType type; 22 | @ApiModelProperty("操作通道") 23 | private String channelId; 24 | @ApiModelProperty("交易ID") 25 | private String TransactionID; 26 | @ApiModelProperty("验证状态码") 27 | private byte ValidationCode; 28 | @ApiModelProperty("时间戳") 29 | private Comparable Timestamp; 30 | @ApiModelProperty("创建者") 31 | private BlockInfo.EnvelopeInfo.IdentitiesInfo Creator; 32 | @ApiModelProperty("随机值") 33 | private byte[] Nonce; 34 | @ApiModelProperty("写操作") 35 | private Map ws = new HashMap<>(); 36 | 37 | public EnvelopeModel(BlockInfo.EnvelopeInfo envelopeInfo) { 38 | this.type = envelopeInfo.getType(); 39 | this.channelId = envelopeInfo.getChannelId(); 40 | this.TransactionID = envelopeInfo.getTransactionID(); 41 | this.ValidationCode = envelopeInfo.getValidationCode(); 42 | this.Creator = envelopeInfo.getCreator(); 43 | this.Timestamp = envelopeInfo.getTimestamp(); 44 | this.Nonce = envelopeInfo.getNonce(); 45 | if (envelopeInfo.getType() == BlockInfo.EnvelopeType.TRANSACTION_ENVELOPE) { //交易信封 46 | BlockInfo.TransactionEnvelopeInfo txeInfo = (BlockInfo.TransactionEnvelopeInfo) envelopeInfo; 47 | int txCount = txeInfo.getTransactionActionInfoCount(); 48 | //遍历所有交易 49 | for (int i = 0; i < txCount; i++) { 50 | BlockInfo.TransactionEnvelopeInfo.TransactionActionInfo txInfo = txeInfo.getTransactionActionInfo(i); 51 | //读写操作 52 | TxReadWriteSetInfo rwsetInfo = txInfo.getTxReadWriteSet(); 53 | if (null != rwsetInfo) { 54 | for (TxReadWriteSetInfo.NsRwsetInfo nsRwsetInfo : rwsetInfo.getNsRwsetInfos()) { 55 | final String namespace = nsRwsetInfo.getNamespace(); 56 | KvRwset.KVRWSet rws = null; 57 | try { 58 | rws = nsRwsetInfo.getRwset(); 59 | } catch (InvalidProtocolBufferException e) { 60 | e.printStackTrace(); 61 | } 62 | int rs = -1; 63 | assert rws != null; 64 | for (KvRwset.KVRead readList : rws.getReadsList()) { 65 | rs++; 66 | } 67 | for (KvRwset.KVWrite writeList : rws.getWritesList()) { 68 | try { 69 | ws.put(writeList.getKey(), ChaincodeManager.printableString(new String(writeList.getValue().toByteArray(), "UTF-8"))); 70 | } catch (UnsupportedEncodingException e) { 71 | e.printStackTrace(); 72 | } 73 | } 74 | } 75 | } 76 | 77 | } 78 | } 79 | } 80 | 81 | public BlockInfo.EnvelopeType getType() { 82 | return type; 83 | } 84 | 85 | public String getChannelId() { 86 | return channelId; 87 | } 88 | 89 | public String getTransactionID() { 90 | return TransactionID; 91 | } 92 | 93 | public byte getValidationCode() { 94 | return ValidationCode; 95 | } 96 | 97 | public Comparable getTimestamp() { 98 | return Timestamp; 99 | } 100 | 101 | public Map getWs() { 102 | return ws; 103 | } 104 | 105 | public void setWs(Map ws) { 106 | this.ws = ws; 107 | } 108 | 109 | public BlockInfo.EnvelopeInfo.IdentitiesInfo getCreator() { 110 | return Creator; 111 | } 112 | 113 | public byte[] getNonce() { 114 | return Nonce; 115 | } 116 | 117 | public EnvelopeModel() { 118 | } 119 | 120 | public void setType(BlockInfo.EnvelopeType type) { 121 | this.type = type; 122 | } 123 | 124 | public void setChannelId(String channelId) { 125 | this.channelId = channelId; 126 | } 127 | 128 | public void setTransactionID(String transactionID) { 129 | TransactionID = transactionID; 130 | } 131 | 132 | public void setValidationCode(byte validationCode) { 133 | ValidationCode = validationCode; 134 | } 135 | 136 | public void setTimestamp(Comparable timestamp) { 137 | Timestamp = timestamp; 138 | } 139 | 140 | public void setCreator(BlockInfo.EnvelopeInfo.IdentitiesInfo creator) { 141 | Creator = creator; 142 | } 143 | 144 | public void setNonce(byte[] nonce) { 145 | Nonce = nonce; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/sdk/trace/FabricUser.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.sdk.trace; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.io.ObjectInputStream; 7 | import java.io.ObjectOutputStream; 8 | import java.io.Serializable; 9 | import java.util.Set; 10 | 11 | import org.bouncycastle.util.encoders.Hex; 12 | import org.hyperledger.fabric.sdk.Enrollment; 13 | import org.hyperledger.fabric.sdk.User; 14 | 15 | import io.netty.util.internal.StringUtil; 16 | 17 | /** 18 | * 联盟用户对象 19 | * 20 | * @author 杨毅 21 | * 22 | * @date 2017年9月7日 - 下午4:36:53 23 | * @email abericyang@gmail.com 24 | */ 25 | public class FabricUser implements User, Serializable { 26 | 27 | private static final long serialVersionUID = 5695080465408336815L; 28 | 29 | /** 名称 */ 30 | private String name; 31 | /** 规则 */ 32 | private Set roles; 33 | /** 账户 */ 34 | private String account; 35 | /** 从属联盟 */ 36 | private String affiliation; 37 | /** 组织 */ 38 | private String organization; 39 | /** 注册操作的密�? */ 40 | private String enrollmentSecret; 41 | /** 会员id */ 42 | private String mspId; 43 | /** 注册登记操作 */ 44 | Enrollment enrollment = null; 45 | 46 | /** 存储配置对象 */ 47 | private transient FabricStore keyValStore; 48 | private String keyValStoreName; 49 | 50 | public FabricUser(String name, String org, FabricStore store) { 51 | this.name = name; 52 | this.keyValStore = store; 53 | this.organization = org; 54 | this.keyValStoreName = toKeyValStoreName(this.name, org); 55 | 56 | String memberStr = keyValStore.getValue(keyValStoreName); 57 | if (null != memberStr) { 58 | saveState(); 59 | } else { 60 | restoreState(); 61 | } 62 | } 63 | 64 | /** 65 | * 设置账户信息并将用户状�?�更新至存储配置对象 66 | * 67 | * @param account 68 | * 账户 69 | */ 70 | public void setAccount(String account) { 71 | this.account = account; 72 | saveState(); 73 | } 74 | 75 | @Override 76 | public String getAccount() { 77 | return this.account; 78 | } 79 | 80 | /** 81 | * 设置从属联盟信息并将用户状�?�更新至存储配置对象 82 | * 83 | * @param affiliation 84 | * 从属联盟 85 | */ 86 | public void setAffiliation(String affiliation) { 87 | this.affiliation = affiliation; 88 | saveState(); 89 | } 90 | 91 | @Override 92 | public String getAffiliation() { 93 | return this.affiliation; 94 | } 95 | 96 | @Override 97 | public Enrollment getEnrollment() { 98 | return this.enrollment; 99 | } 100 | 101 | /** 102 | * 设置会员id信息并将用户状�?�更新至存储配置对象 103 | * 104 | * @param mspID 105 | * 会员id 106 | */ 107 | public void setMspId(String mspID) { 108 | this.mspId = mspID; 109 | saveState(); 110 | } 111 | 112 | @Override 113 | public String getMspId() { 114 | return this.mspId; 115 | } 116 | 117 | @Override 118 | public String getName() { 119 | return this.name; 120 | } 121 | 122 | /** 123 | * 设置规则信息并将用户状�?�更新至存储配置对象 124 | * 125 | * @param roles 126 | * 规则 127 | */ 128 | public void setRoles(Set roles) { 129 | this.roles = roles; 130 | saveState(); 131 | } 132 | 133 | @Override 134 | public Set getRoles() { 135 | return this.roles; 136 | } 137 | 138 | public String getEnrollmentSecret() { 139 | return enrollmentSecret; 140 | } 141 | 142 | /** 143 | * 设置注册操作的密钥信息并将用户状态更新至存储配置对象 144 | * 145 | * @param enrollmentSecret 146 | * 注册操作的密�? 147 | */ 148 | public void setEnrollmentSecret(String enrollmentSecret) { 149 | this.enrollmentSecret = enrollmentSecret; 150 | saveState(); 151 | } 152 | 153 | /** 154 | * 设置注册登记操作信息并将用户状�?�更新至存储配置对象 155 | * 156 | * @param enrollment 157 | * 注册登记操作 158 | */ 159 | public void setEnrollment(Enrollment enrollment) { 160 | this.enrollment = enrollment; 161 | saveState(); 162 | } 163 | 164 | /** 165 | * 确定这个名称是否已注�? 166 | * 167 | * @return 与否 168 | */ 169 | public boolean isRegistered() { 170 | return !StringUtil.isNullOrEmpty(enrollmentSecret); 171 | } 172 | 173 | /** 174 | * 确定这个名字是否已经注册 175 | * 176 | * @return 与否 177 | */ 178 | public boolean isEnrolled() { 179 | return this.enrollment != null; 180 | } 181 | 182 | /** 将用户状态保存至存储配置对象 */ 183 | public void saveState() { 184 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 185 | try { 186 | ObjectOutputStream oos = new ObjectOutputStream(bos); 187 | oos.writeObject(this); 188 | oos.flush(); 189 | keyValStore.setValue(keyValStoreName, Hex.toHexString(bos.toByteArray())); 190 | bos.close(); 191 | } catch (IOException e) { 192 | e.printStackTrace(); 193 | } 194 | } 195 | 196 | /** 197 | * 从键值存储中恢复该用户的状�??(如果找到的话)。如果找不到,什么也不要做�?? 198 | * 199 | * @return 返回用户 200 | */ 201 | private FabricUser restoreState() { 202 | String memberStr = keyValStore.getValue(keyValStoreName); 203 | if (null != memberStr) { 204 | // 用户在键值存储中被找到,因此恢复状�?��?? 205 | byte[] serialized = Hex.decode(memberStr); 206 | ByteArrayInputStream bis = new ByteArrayInputStream(serialized); 207 | try { 208 | ObjectInputStream ois = new ObjectInputStream(bis); 209 | FabricUser state = (FabricUser) ois.readObject(); 210 | if (state != null) { 211 | this.name = state.name; 212 | this.roles = state.roles; 213 | this.account = state.account; 214 | this.affiliation = state.affiliation; 215 | this.organization = state.organization; 216 | this.enrollmentSecret = state.enrollmentSecret; 217 | this.enrollment = state.enrollment; 218 | this.mspId = state.mspId; 219 | return this; 220 | } 221 | } catch (Exception e) { 222 | throw new RuntimeException(String.format("Could not restore state of member %s", this.name), e); 223 | } 224 | } 225 | return null; 226 | } 227 | 228 | public static String toKeyValStoreName(String name, String org) { 229 | System.out.println("toKeyValStoreName = " + "user." + name + org); 230 | return "user." + name + org; 231 | } 232 | 233 | } 234 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/sdk/util/DateUtil.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.sdk.util; 2 | 3 | import java.text.ParseException; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Calendar; 6 | import java.util.Date; 7 | import java.util.Locale; 8 | 9 | /** 10 | * 一般日期(时间)处理通用公共方法类 11 | * 12 | * @author 杨毅 13 | * 14 | * @date 2017年10月19日 - 下午5:33:00 15 | * @email abericyang@gmail.com 16 | */ 17 | public class DateUtil { 18 | 19 | private static DateUtil instance; 20 | 21 | public static DateUtil obtain() { 22 | if (null == instance) { 23 | synchronized (DateUtil.class) { 24 | if (null == instance) { 25 | instance = new DateUtil(); 26 | } 27 | } 28 | } 29 | return instance; 30 | } 31 | 32 | private DateUtil() { 33 | } 34 | 35 | // 使用DateFormat 36 | 37 | /** 38 | * 获取当前时间 39 | * 40 | * @param format 41 | * (yyyy年MM月dd日 HH时mm分ss秒|yyyy年MM月dd日 42 | * HH时mm分ss秒|yyyyMMddHHmmss|HH:mm:ss|yyyy年MM月dd日 等) 43 | * 44 | * @return 当前时间格式 45 | */ 46 | public String getCurrentDate(String format) { 47 | SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.CHINA); 48 | return sdf.format(new Date()); 49 | } 50 | 51 | /** 52 | * 将字符串转换为日期 53 | * 54 | * @param dateStr 55 | * 实际日期字符串 56 | * @param format 57 | * 指定日期字符串格式 58 | * 59 | * @return date 60 | */ 61 | public Date parseStringToDate(String dateStr, String format) throws Exception { 62 | SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.CHINA); 63 | return sdf.parse(dateStr); 64 | } 65 | 66 | /** 67 | * 将日期转换为字符串 68 | * 69 | * @param date 70 | * date日期 71 | * @param format 72 | * 日期格式 73 | * 74 | * @return 日期字符串 75 | */ 76 | public String parseDateFormat(Date date, String format) { 77 | try { 78 | SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.CHINA); 79 | return sdf.format(date); 80 | } catch (Exception ex) { 81 | return ""; 82 | } 83 | } 84 | 85 | /** 86 | * 获得指定日期的前一天 87 | * 88 | * @param specifiedDay 89 | * 指定日期 90 | * 91 | * @return 指定日期的前一天 92 | */ 93 | public String getSpecifiedDayBefore(String specifiedDay) { 94 | Calendar c = Calendar.getInstance(); 95 | Date date = null; 96 | try { 97 | date = new SimpleDateFormat("yy-MM-dd", Locale.CHINA).parse(specifiedDay); 98 | } catch (ParseException e) { 99 | e.printStackTrace(); 100 | } 101 | c.setTime(date); 102 | int day = c.get(Calendar.DATE); 103 | c.set(Calendar.DATE, day - 1); 104 | 105 | return new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(c.getTime()); 106 | } 107 | 108 | /** 109 | * 返回日期中的指定部分--YYYY 年份,MM 月份,DD 天,HH 小时 MI 分 SS 秒 110 | * 111 | * @param date 112 | * date日期 113 | * @param type 114 | * 日期中的指定部分 115 | * 116 | * @return 日期中的指定部分值 117 | */ 118 | public int getDatePart(Date date, DatePartType type) { 119 | Calendar cal = Calendar.getInstance(); 120 | cal.setTime(date); 121 | switch (type) { 122 | case Year: 123 | return cal.get(Calendar.YEAR); 124 | case Month: 125 | return cal.get(Calendar.MONTH); 126 | case Day: 127 | return cal.get(Calendar.DAY_OF_MONTH); 128 | case Hour: 129 | return cal.get(Calendar.HOUR_OF_DAY); 130 | case Minute: 131 | return cal.get(Calendar.MINUTE); 132 | case Second: 133 | return cal.get(Calendar.SECOND); 134 | default: 135 | return 0; 136 | } 137 | } 138 | 139 | public enum DatePartType { 140 | Year, Month, Day, Hour, Minute, Second 141 | } 142 | 143 | /** 144 | * 计算两个日期之间的时间差 传入的对象为两个日期,字符串和时间对象即可 145 | * 146 | * @param dateFrom 147 | * 起始日期/字符串/时间对象 148 | * @param dateTo 149 | * 截止日期/字符串/时间对象 150 | * 151 | * @return 时间差 152 | * 153 | * @throws Exception 154 | */ 155 | public String dateDiff(Object dateFrom, Object dateTo) throws Exception { 156 | Date date1 = getDateByObject(dateFrom); 157 | Date date2 = getDateByObject(dateTo); 158 | Calendar cal1 = Calendar.getInstance(); 159 | Calendar cal2 = Calendar.getInstance(); 160 | cal1.setTime(date2); 161 | cal2.setTime(date1); 162 | float a = ((float) (cal1.getTimeInMillis() - cal2.getTimeInMillis()) / 86400) / 1000; 163 | int b = (int) a; 164 | return "" + Math.abs(((b == a) ? b : b + 1)); 165 | } 166 | 167 | /** 168 | * 根据传入参数产生日期对象 169 | * 170 | * @param obj 171 | * 传入参数 172 | * 173 | * @return Date 174 | * 175 | * @throws Exception 176 | */ 177 | public Date getDateByObject(Object obj) throws Exception { 178 | if (null == obj) { 179 | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA).parse(getCurrentDate(null)); 180 | } 181 | 182 | if (obj instanceof Date) { 183 | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA).parse(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA).format(obj)); 184 | } 185 | if (obj instanceof String) { 186 | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA).parse((String) obj); 187 | } 188 | return null; 189 | } 190 | 191 | /** 192 | * 获取当前年/月/日 193 | * 194 | * @return yyyy/MM/dd 195 | */ 196 | public String getNowDateFor(String dateFro) { 197 | SimpleDateFormat sdf = new SimpleDateFormat(dateFro, Locale.CHINA); 198 | return sdf.format(new Date()); 199 | } 200 | 201 | /** 202 | * 功能描述:返回小时 203 | * 204 | * @param date 205 | * 日期 206 | * 207 | * @return 返回小时 208 | */ 209 | public int getHour(Date date) { 210 | Calendar calendar = Calendar.getInstance(); 211 | calendar.setTime(date); 212 | return calendar.get(Calendar.HOUR_OF_DAY); 213 | } 214 | 215 | /** 216 | * 功能描述:将字符串转换为long型 217 | * 218 | * @param string 219 | * 日期 220 | * @param pattern 221 | * 日期格式 222 | * 223 | * @return 返回long型时间 224 | */ 225 | public long parseDate(String string, String pattern) { 226 | SimpleDateFormat timeLine = new SimpleDateFormat(pattern, Locale.CHINA); 227 | long l = 0; 228 | try { 229 | l = timeLine.parse(string).getTime(); 230 | return l; 231 | } catch (ParseException e) { 232 | System.out.print("parseDate 时间解析错误"); 233 | } 234 | return l; 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /sdk/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 124 | FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( 125 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | echo Found %WRAPPER_JAR% 132 | ) else ( 133 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 134 | echo Downloading from: %DOWNLOAD_URL% 135 | powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" 136 | echo Finished downloading %WRAPPER_JAR% 137 | ) 138 | @REM End of extension 139 | 140 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 141 | if ERRORLEVEL 1 goto error 142 | goto end 143 | 144 | :error 145 | set ERROR_CODE=1 146 | 147 | :end 148 | @endlocal & set ERROR_CODE=%ERROR_CODE% 149 | 150 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 151 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 152 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 153 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 154 | :skipRcPost 155 | 156 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 157 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 158 | 159 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 160 | 161 | exit /B %ERROR_CODE% 162 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/controller/home/UserController.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.controller.home; 2 | 3 | import cn.huse.trace.web.common.ReturnMessageMap; 4 | import cn.huse.trace.web.common.Utils; 5 | import cn.huse.trace.web.common.auth.jwt.JwtUserTokenUtil; 6 | import cn.huse.trace.web.config.parsetoken.ParseToken; 7 | import cn.huse.trace.web.dao.DaoException; 8 | import cn.huse.trace.web.entity.Transaction; 9 | import cn.huse.trace.web.entity.User; 10 | import cn.huse.trace.web.service.ProjectService; 11 | import cn.huse.trace.web.service.TransactionService; 12 | import cn.huse.trace.web.service.UserService; 13 | import io.swagger.annotations.Api; 14 | import io.swagger.annotations.ApiOperation; 15 | import io.swagger.annotations.ApiParam; 16 | import org.springframework.beans.factory.annotation.Value; 17 | import org.springframework.validation.annotation.Validated; 18 | import org.springframework.web.bind.annotation.*; 19 | import org.springframework.web.multipart.MultipartFile; 20 | 21 | import javax.annotation.Resource; 22 | import java.io.File; 23 | 24 | /** 25 | * @author: huanxi 26 | * @date: 2019/4/27 22:25 27 | */ 28 | @RestController 29 | @RequestMapping("/user") 30 | @Api(value = "用户接口", description = "用户接口") 31 | public class UserController { 32 | @Resource 33 | UserService userService; 34 | @Resource 35 | ProjectService projectService; 36 | @Resource 37 | TransactionService transactionService; 38 | @Value("${upload.path}") 39 | String uploadPath; 40 | 41 | @PostMapping("") 42 | @ApiOperation("用户注册") 43 | public ReturnMessageMap addUser(@Validated User user) { 44 | try { 45 | userService.addUser(user); 46 | } catch (DaoException e) { 47 | return new ReturnMessageMap(e.getMessage()); 48 | } 49 | return new ReturnMessageMap("register successfully"); 50 | } 51 | 52 | @PostMapping("/token") 53 | @ApiOperation("用户登入获取token") 54 | public ReturnMessageMap addToken(@Validated User user) { 55 | User user1 = userService.getUser(user.getAccount()); 56 | if (user1 == null) return new ReturnMessageMap(4010, "not exist this user"); 57 | if (user1.getPassword().equals(Utils.passwd(user.getPassword()))) { 58 | String token = JwtUserTokenUtil.generateToken(user1); 59 | return new ReturnMessageMap(token); 60 | } 61 | return new ReturnMessageMap(4011, "username or password error"); 62 | } 63 | 64 | @GetMapping("/info") 65 | @ApiOperation("获取用户信息") 66 | public ReturnMessageMap getUserInfo(@ParseToken String userId) { 67 | return new ReturnMessageMap(userService.getUser(userId)); 68 | } 69 | 70 | @PutMapping("/info") 71 | @ApiOperation("修改用户信息") 72 | public ReturnMessageMap updateUserInfo(User user, @ParseToken String userId) throws DaoException { 73 | user.setAccount(userId); 74 | userService.update(user); 75 | return new ReturnMessageMap("update successfully!"); 76 | } 77 | 78 | 79 | @PostMapping("/header") 80 | @ApiOperation("修改用户头像") 81 | public ReturnMessageMap updateUserHeader(@ParseToken String userId, MultipartFile image) throws DaoException { 82 | File file; 83 | try { 84 | file = Utils.saveFile(image, uploadPath); 85 | } catch (Exception e) { 86 | return new ReturnMessageMap(5013, e.getMessage()); 87 | } 88 | if (file == null) return new ReturnMessageMap("upload file failed!"); 89 | User user = userService.getUser(userId); 90 | if (user != null) { 91 | user.setHeaderUrl(file.getName()); 92 | userService.update(user); 93 | } 94 | return new ReturnMessageMap(user); 95 | } 96 | 97 | @GetMapping("projects") 98 | @ApiOperation("查看我发布的项目") 99 | public ReturnMessageMap getMyProjects(@ParseToken String userId) { 100 | return new ReturnMessageMap(projectService.queryByUserId(userId)); 101 | } 102 | 103 | @PostMapping("charge") 104 | @ApiOperation("模拟充值") 105 | public ReturnMessageMap charge(@ParseToken String userId, @ApiParam("充值金额") float amount) { 106 | if (amount <= 0) return new ReturnMessageMap(4011, "amount must be gt 0"); 107 | Transaction transaction = new Transaction(); 108 | transaction.setAmount(amount); 109 | transaction.setInId("bank"); 110 | transaction.setOutId(userId); 111 | try { 112 | transactionService.addTransaction(transaction); 113 | } catch (DaoException e) { 114 | return new ReturnMessageMap(5021, e.getMessage()); 115 | } 116 | return new ReturnMessageMap("ok"); 117 | } 118 | 119 | @PostMapping("withdraw") 120 | @ApiOperation("模拟提现") 121 | public ReturnMessageMap withdraw(@ParseToken String userId, @ApiParam("充值金额") float amount) { 122 | Transaction transaction = new Transaction(); 123 | transaction.setAmount(amount); 124 | transaction.setInId(userId); 125 | transaction.setOutId("bank"); 126 | try { 127 | transactionService.addTransaction(transaction); 128 | } catch (DaoException e) { 129 | return new ReturnMessageMap(5021, e.getMessage()); 130 | } 131 | return new ReturnMessageMap("ok"); 132 | } 133 | 134 | @PostMapping("transfer") 135 | @ApiOperation("模拟投资众筹") 136 | public ReturnMessageMap transfer(@ParseToken String userId, @ApiParam("转账金额") float amount, @ApiParam("众筹项目") String project) { 137 | return new ReturnMessageMap(projectService.queryByUserId(userId)); 138 | } 139 | 140 | @GetMapping("balance") 141 | @ApiOperation("查询余额") 142 | public ReturnMessageMap getBalance(@ParseToken String userId) { 143 | return new ReturnMessageMap(transactionService.getBalance(userId)); 144 | } 145 | 146 | @GetMapping("transaction") 147 | @ApiOperation("获取我的所有交易") 148 | public ReturnMessageMap getTransaction(@ParseToken String userId) { 149 | return new ReturnMessageMap(transactionService.getTransactionByUserId(userId)); 150 | } 151 | 152 | @GetMapping("transaction/in") 153 | @ApiOperation("获取转给我的所有交易") 154 | public ReturnMessageMap getTransactionIn(@ParseToken String userId) { 155 | return new ReturnMessageMap(transactionService.getTransactionInByUserId(userId)); 156 | } 157 | 158 | @GetMapping("transaction/out") 159 | @ApiOperation("获取我转出的所有交易") 160 | public ReturnMessageMap getTransactionOut(@ParseToken String userId) { 161 | return new ReturnMessageMap(transactionService.getTransactionOutByUserId(userId)); 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/sdk/trace/FabricStore.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.sdk.trace; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.OutputStream; 10 | import java.io.Reader; 11 | import java.io.Serializable; 12 | import java.io.StringReader; 13 | import java.security.NoSuchAlgorithmException; 14 | import java.security.NoSuchProviderException; 15 | import java.security.PrivateKey; 16 | import java.security.Security; 17 | import java.security.spec.InvalidKeySpecException; 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | import java.util.Properties; 21 | 22 | import org.apache.commons.io.IOUtils; 23 | import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; 24 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 25 | import org.bouncycastle.openssl.PEMParser; 26 | import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; 27 | import org.hyperledger.fabric.sdk.Enrollment; 28 | 29 | /** 30 | * 联盟存储配置对象 31 | * 32 | * @author 杨毅 33 | * 34 | * @date 2017年9月7日 - 下午4:36:19 35 | * @email abericyang@gmail.com 36 | */ 37 | public class FabricStore { 38 | 39 | private String file; 40 | /** 用户信息集合 */ 41 | private final Map members = new HashMap<>(); 42 | 43 | public FabricStore(File file) { 44 | this.file = file.getAbsolutePath(); 45 | } 46 | 47 | /** 48 | * 设置与名称相关的值 49 | * 50 | * @param name 51 | * 名称 52 | * @param value 53 | * 相关值 54 | */ 55 | public void setValue(String name, String value) { 56 | Properties properties = loadProperties(); 57 | try (OutputStream output = new FileOutputStream(file)) { 58 | properties.setProperty(name, value); 59 | properties.store(output, ""); 60 | output.close(); 61 | } catch (IOException e) { 62 | System.out.println(String.format("Could not save the keyvalue store, reason:%s", e.getMessage())); 63 | } 64 | } 65 | 66 | /** 67 | * 获取与名称相关的值 68 | * 69 | * @param 名称 70 | * @return 相关值 71 | */ 72 | public String getValue(String name) { 73 | Properties properties = loadProperties(); 74 | return properties.getProperty(name); 75 | } 76 | 77 | /** 78 | * 加载配置文件 79 | * 80 | * @return 配置文件对象 81 | */ 82 | private Properties loadProperties() { 83 | Properties properties = new Properties(); 84 | try (InputStream input = new FileInputStream(file)) { 85 | properties.load(input); 86 | input.close(); 87 | } catch (FileNotFoundException e) { 88 | System.out.println(String.format("Could not find the file \"%s\"", file)); 89 | } catch (IOException e) { 90 | System.out.println(String.format("Could not load keyvalue store from file \"%s\", reason:%s", file, e.getMessage())); 91 | } 92 | return properties; 93 | } 94 | 95 | /** 96 | * 用给定的名称获取用户 97 | * 98 | * @param 名称 99 | * @param 组织 100 | * 101 | * @return 用户 102 | */ 103 | public FabricUser getMember(String name, String org) { 104 | // 尝试从缓存中获取User状�?? 105 | FabricUser fabricUser = members.get(FabricUser.toKeyValStoreName(name, org)); 106 | if (null != fabricUser) { 107 | return fabricUser; 108 | } 109 | // 创建User,并尝试从键值存储中恢复它的状�??(如果找到的话)�? 110 | fabricUser = new FabricUser(name, org, this); 111 | return fabricUser; 112 | } 113 | 114 | /** 115 | * 用给定的名称获取用户 116 | * 117 | * @param name 118 | * 名称 119 | * @param org 120 | * 组织 121 | * @param mspId 122 | * 会员id 123 | * @param privateKeyFile 124 | * @param certificateFile 125 | * 126 | * @return user 用户 127 | * 128 | * @throws IOException 129 | * @throws NoSuchAlgorithmException 130 | * @throws NoSuchProviderException 131 | * @throws InvalidKeySpecException 132 | */ 133 | public FabricUser getMember(String name, String org, String mspId, File privateKeyFile, File certificateFile) 134 | throws IOException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException { 135 | try { 136 | // 尝试从缓存中获取User状�?? 137 | FabricUser fabricUser = members.get(FabricUser.toKeyValStoreName(name, org)); 138 | if (null != fabricUser) { 139 | System.out.println("尝试从缓存中获取User状�?? User = " + fabricUser); 140 | return fabricUser; 141 | } 142 | // 创建User,并尝试从键值存储中恢复它的状�??(如果找到的话)�? 143 | fabricUser = new FabricUser(name, org, this); 144 | fabricUser.setMspId(mspId); 145 | String certificate = new String(IOUtils.toByteArray(new FileInputStream(certificateFile)), "UTF-8"); 146 | PrivateKey privateKey = getPrivateKeyFromBytes(IOUtils.toByteArray(new FileInputStream(privateKeyFile))); 147 | fabricUser.setEnrollment(new StoreEnrollement(privateKey, certificate)); 148 | fabricUser.saveState(); 149 | return fabricUser; 150 | } catch (IOException e) { 151 | e.printStackTrace(); 152 | throw e; 153 | } catch (NoSuchAlgorithmException e) { 154 | e.printStackTrace(); 155 | throw e; 156 | } catch (NoSuchProviderException e) { 157 | e.printStackTrace(); 158 | throw e; 159 | } catch (InvalidKeySpecException e) { 160 | e.printStackTrace(); 161 | throw e; 162 | } catch (ClassCastException e) { 163 | e.printStackTrace(); 164 | throw e; 165 | } 166 | } 167 | 168 | /** 169 | * 通过字节数组信息获取私钥 170 | * 171 | * @param data 172 | * 字节数组 173 | * 174 | * @return 私钥 175 | * 176 | * @throws IOException 177 | * @throws NoSuchProviderException 178 | * @throws NoSuchAlgorithmException 179 | * @throws InvalidKeySpecException 180 | */ 181 | private PrivateKey getPrivateKeyFromBytes(byte[] data) throws IOException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException { 182 | final Reader pemReader = new StringReader(new String(data)); 183 | final PrivateKeyInfo pemPair; 184 | try (PEMParser pemParser = new PEMParser(pemReader)) { 185 | pemPair = (PrivateKeyInfo) pemParser.readObject(); 186 | } 187 | PrivateKey privateKey = new JcaPEMKeyConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getPrivateKey(pemPair); 188 | return privateKey; 189 | } 190 | 191 | static { 192 | try { 193 | Security.addProvider(new BouncyCastleProvider()); 194 | } catch (Exception e) { 195 | e.printStackTrace(); 196 | } 197 | } 198 | 199 | /** 200 | * 自定义注册登记操作类 201 | * 202 | * @author yangyi47 203 | * 204 | */ 205 | static final class StoreEnrollement implements Enrollment, Serializable { 206 | 207 | private static final long serialVersionUID = 6965341351799577442L; 208 | 209 | /** 私钥 */ 210 | private final PrivateKey privateKey; 211 | /** 授权证书 */ 212 | private final String certificate; 213 | 214 | StoreEnrollement(PrivateKey privateKey, String certificate) { 215 | this.certificate = certificate; 216 | this.privateKey = privateKey; 217 | } 218 | 219 | @Override 220 | public PrivateKey getKey() { 221 | return privateKey; 222 | } 223 | 224 | @Override 225 | public String getCert() { 226 | return certificate; 227 | } 228 | } 229 | 230 | } 231 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/web/cache/RedisDao.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.web.cache; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.data.redis.core.RedisTemplate; 5 | import org.springframework.stereotype.Component; 6 | 7 | import javax.annotation.Resource; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | /** 13 | * @author: huanxi 14 | * @date: 2019-04-19 21:25 15 | */ 16 | @Component 17 | @Slf4j 18 | public final class RedisDao { 19 | @Resource 20 | private RedisTemplate redisTemplate; 21 | 22 | /** 23 | * 获取普通缓存 24 | * 25 | * @param key 26 | * @return 27 | */ 28 | public Object get(String key) { 29 | return key == null ? null : redisTemplate.opsForValue().get(key); 30 | } 31 | 32 | 33 | /** 34 | * 设置普通缓存 35 | * 36 | * @param key 37 | * @return 38 | */ 39 | public boolean set(String key, Object value) { 40 | try { 41 | redisTemplate.opsForValue().set(key, value); 42 | return true; 43 | } catch (Exception e) { 44 | e.printStackTrace(); 45 | return false; 46 | } 47 | } 48 | 49 | /** 50 | * 指定缓存失效时间 51 | * 52 | * @param key 键 53 | * @param time 时间(秒) 54 | */ 55 | public boolean expire(String key, long time) { 56 | try { 57 | if (time > 0) { 58 | redisTemplate.expire(key, time, TimeUnit.SECONDS); 59 | } 60 | return true; 61 | } catch (Exception e) { 62 | e.printStackTrace(); 63 | return false; 64 | } 65 | } 66 | 67 | /** 68 | * 根据key 获取过期时间 69 | * 70 | * @param key 键 不能为null 71 | * @return 时间(秒) 返回0代表为永久有效 72 | */ 73 | public long getExpire(String key) { 74 | return redisTemplate.getExpire(key, TimeUnit.SECONDS); 75 | } 76 | 77 | /** 78 | * 向一张hash表中放入数据,如果不存在将创建 79 | * 80 | * @param key 键 81 | * @param item 项 82 | * @param value 值 83 | * @return true 成功 false失败 84 | */ 85 | public boolean hset(String key, String item, Object value) { 86 | try { 87 | redisTemplate.opsForHash().put(key, item, value); 88 | return true; 89 | } catch (Exception e) { 90 | log.error("写redis缓存失败" + e.getMessage()); 91 | return false; 92 | } 93 | } 94 | 95 | /** 96 | * 向一张hash表中放入数据,如果不存在将创建 97 | * 98 | * @param key 键 99 | * @param item 项 100 | * @param value 值 101 | * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 102 | * @return true 成功 false失败 103 | */ 104 | public boolean hset(String key, String item, Object value, long time) { 105 | try { 106 | redisTemplate.opsForHash().put(key, item, value); 107 | if (time > 0) { 108 | expire(key, time); 109 | } 110 | return true; 111 | } catch (Exception e) { 112 | e.printStackTrace(); 113 | return false; 114 | } 115 | } 116 | 117 | /** 118 | * 删除hash表中的值 119 | * 120 | * @param key 键 不能为null 121 | * @param item 项 可以使多个 不能为null 122 | */ 123 | public void hdel(String key, Object... item) { 124 | redisTemplate.opsForHash().delete(key, item); 125 | } 126 | 127 | /** 128 | * HashGet 129 | * 130 | * @param key 键 不能为null 131 | * @param item 项 不能为null 132 | * @return 值 133 | */ 134 | public Object hget(String key, String item) { 135 | return redisTemplate.opsForHash().get(key, item); 136 | } 137 | 138 | /** 139 | * 获取hashKey对应的所有键值 140 | * 141 | * @param key 键 142 | * @return 对应的多个键值 143 | */ 144 | public Map hmget(String key) { 145 | return redisTemplate.opsForHash().entries(key); 146 | } 147 | 148 | /** 149 | * 删除 150 | */ 151 | public void delete(String key) { 152 | redisTemplate.delete(key); 153 | } 154 | 155 | 156 | /** 157 | * 获取list缓存的内容 158 | * 159 | * @param key 键 160 | * @param start 开始 161 | * @param end 结束 0 到 -1代表所有值 162 | */ 163 | 164 | public List lGet(String key, long start, long end) { 165 | try { 166 | return redisTemplate.opsForList().range(key, start, end); 167 | } catch (Exception e) { 168 | e.printStackTrace(); 169 | return null; 170 | } 171 | } 172 | 173 | /** 174 | * 获取list缓存的长度 175 | * 176 | * @param key 键 177 | * @return 405 178 | */ 179 | 180 | public long lGetListSize(String key) { 181 | try { 182 | return redisTemplate.opsForList().size(key); 183 | } catch (Exception e) { 184 | e.printStackTrace(); 185 | return 0; 186 | } 187 | } 188 | 189 | /** 190 | * 通过索引 获取list中的值 191 | * 192 | * @param key 键 193 | * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 194 | * @return 420 195 | */ 196 | 197 | public Object lGetIndex(String key, long index) { 198 | try { 199 | return redisTemplate.opsForList().index(key, index); 200 | } catch (Exception e) { 201 | e.printStackTrace(); 202 | return null; 203 | } 204 | } 205 | 206 | /** 207 | * 将list放入缓存 208 | * @param key 键 209 | * @param value 值 210 | * @return 436 211 | */ 212 | public boolean lSet(String key, Object value) { 213 | try { 214 | redisTemplate.opsForList().rightPush(key, value); 215 | return true; 216 | } catch (Exception e) { 217 | e.printStackTrace(); 218 | return false; 219 | } 220 | } 221 | 222 | /** 223 | * 将list放入缓存 224 | * @param key 键 225 | * @param value 值 226 | * @param time 时间(秒) 227 | * @return 228 | */ 229 | 230 | public boolean lSet(String key, Object value, long time) { 231 | try { 232 | redisTemplate.opsForList().rightPush(key, value); 233 | if (time > 0) 234 | expire(key, time); 235 | return true; 236 | } catch (Exception e) { 237 | e.printStackTrace(); 238 | return false; 239 | } 240 | } 241 | 242 | /** 243 | * 将list放入缓存 244 | * 245 | * @param key 键 246 | * @param value 值 247 | * @return 472 248 | */ 249 | 250 | public boolean lSet(String key, List value) { 251 | try { 252 | redisTemplate.opsForList().rightPushAll(key, value); 253 | return true; 254 | } catch (Exception e) { 255 | e.printStackTrace(); 256 | return false; 257 | } 258 | } 259 | 260 | /** 261 | * 将list放入缓存 262 | * 263 | * @param key 键 264 | * @param value 值 265 | * @param time 时间(秒) 266 | * @return 490 267 | */ 268 | public boolean lSet(String key, List value, long time) { 269 | try { 270 | redisTemplate.opsForList().rightPushAll(key, value); 271 | if (time > 0) 272 | expire(key, time); 273 | return true; 274 | } catch (Exception e) { 275 | e.printStackTrace(); 276 | return false; 277 | } 278 | } 279 | 280 | /** 281 | * 根据索引修改list中的某条数据 282 | * 283 | * @param key 键 284 | * @param index 索引 285 | * @param value 值 286 | * @return 509 287 | */ 288 | public boolean lUpdateIndex(String key, long index, Object value) { 289 | try { 290 | redisTemplate.opsForList().set(key, index, value); 291 | return true; 292 | } catch (Exception e) { 293 | e.printStackTrace(); 294 | return false; 295 | } 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /sdk/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | # TODO classpath? 118 | fi 119 | 120 | if [ -z "$JAVA_HOME" ]; then 121 | javaExecutable="`which javac`" 122 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 123 | # readlink(1) is not available as standard on Solaris 10. 124 | readLink=`which readlink` 125 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 126 | if $darwin ; then 127 | javaHome="`dirname \"$javaExecutable\"`" 128 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 129 | else 130 | javaExecutable="`readlink -f \"$javaExecutable\"`" 131 | fi 132 | javaHome="`dirname \"$javaExecutable\"`" 133 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 134 | JAVA_HOME="$javaHome" 135 | export JAVA_HOME 136 | fi 137 | fi 138 | fi 139 | 140 | if [ -z "$JAVACMD" ] ; then 141 | if [ -n "$JAVA_HOME" ] ; then 142 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 143 | # IBM's JDK on AIX uses strange locations for the executables 144 | JAVACMD="$JAVA_HOME/jre/sh/java" 145 | else 146 | JAVACMD="$JAVA_HOME/bin/java" 147 | fi 148 | else 149 | JAVACMD="`which java`" 150 | fi 151 | fi 152 | 153 | if [ ! -x "$JAVACMD" ] ; then 154 | echo "Error: JAVA_HOME is not defined correctly." >&2 155 | echo " We cannot execute $JAVACMD" >&2 156 | exit 1 157 | fi 158 | 159 | if [ -z "$JAVA_HOME" ] ; then 160 | echo "Warning: JAVA_HOME environment variable is not set." 161 | fi 162 | 163 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 164 | 165 | # traverses directory structure from process work directory to filesystem root 166 | # first directory with .mvn subdirectory is considered project base directory 167 | find_maven_basedir() { 168 | 169 | if [ -z "$1" ] 170 | then 171 | echo "Path not specified to find_maven_basedir" 172 | return 1 173 | fi 174 | 175 | basedir="$1" 176 | wdir="$1" 177 | while [ "$wdir" != '/' ] ; do 178 | if [ -d "$wdir"/.mvn ] ; then 179 | basedir=$wdir 180 | break 181 | fi 182 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 183 | if [ -d "${wdir}" ]; then 184 | wdir=`cd "$wdir/.."; pwd` 185 | fi 186 | # end of workaround 187 | done 188 | echo "${basedir}" 189 | } 190 | 191 | # concatenates all lines of a file 192 | concat_lines() { 193 | if [ -f "$1" ]; then 194 | echo "$(tr -s '\n' ' ' < "$1")" 195 | fi 196 | } 197 | 198 | BASE_DIR=`find_maven_basedir "$(pwd)"` 199 | if [ -z "$BASE_DIR" ]; then 200 | exit 1; 201 | fi 202 | 203 | ########################################################################################## 204 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 205 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 206 | ########################################################################################## 207 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 208 | if [ "$MVNW_VERBOSE" = true ]; then 209 | echo "Found .mvn/wrapper/maven-wrapper.jar" 210 | fi 211 | else 212 | if [ "$MVNW_VERBOSE" = true ]; then 213 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 214 | fi 215 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar" 216 | while IFS="=" read key value; do 217 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 218 | esac 219 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 220 | if [ "$MVNW_VERBOSE" = true ]; then 221 | echo "Downloading from: $jarUrl" 222 | fi 223 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 224 | 225 | if command -v wget > /dev/null; then 226 | if [ "$MVNW_VERBOSE" = true ]; then 227 | echo "Found wget ... using wget" 228 | fi 229 | wget "$jarUrl" -O "$wrapperJarPath" 230 | elif command -v curl > /dev/null; then 231 | if [ "$MVNW_VERBOSE" = true ]; then 232 | echo "Found curl ... using curl" 233 | fi 234 | curl -o "$wrapperJarPath" "$jarUrl" 235 | else 236 | if [ "$MVNW_VERBOSE" = true ]; then 237 | echo "Falling back to using Java to download" 238 | fi 239 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 240 | if [ -e "$javaClass" ]; then 241 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 242 | if [ "$MVNW_VERBOSE" = true ]; then 243 | echo " - Compiling MavenWrapperDownloader.java ..." 244 | fi 245 | # Compiling the Java class 246 | ("$JAVA_HOME/bin/javac" "$javaClass") 247 | fi 248 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 249 | # Running the downloader 250 | if [ "$MVNW_VERBOSE" = true ]; then 251 | echo " - Running MavenWrapperDownloader.java ..." 252 | fi 253 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 254 | fi 255 | fi 256 | fi 257 | fi 258 | ########################################################################################## 259 | # End of extension 260 | ########################################################################################## 261 | 262 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 263 | if [ "$MVNW_VERBOSE" = true ]; then 264 | echo $MAVEN_PROJECTBASEDIR 265 | fi 266 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 267 | 268 | # For Cygwin, switch paths to Windows format before running java 269 | if $cygwin; then 270 | [ -n "$M2_HOME" ] && 271 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 272 | [ -n "$JAVA_HOME" ] && 273 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 274 | [ -n "$CLASSPATH" ] && 275 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 276 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 277 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 278 | fi 279 | 280 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 281 | 282 | exec "$JAVACMD" \ 283 | $MAVEN_OPTS \ 284 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 285 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 286 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 287 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/sdk/trace/FabricOrg.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.sdk.trace; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.file.Paths; 6 | import java.security.NoSuchAlgorithmException; 7 | import java.security.NoSuchProviderException; 8 | import java.security.spec.InvalidKeySpecException; 9 | import java.util.Collection; 10 | import java.util.Collections; 11 | import java.util.HashMap; 12 | import java.util.HashSet; 13 | import java.util.Map; 14 | import java.util.Properties; 15 | import java.util.Set; 16 | 17 | import cn.huse.trace.sdk.trace.bean.Orderers; 18 | import cn.huse.trace.sdk.trace.bean.Peers; 19 | import org.hyperledger.fabric.sdk.Peer; 20 | import org.hyperledger.fabric.sdk.User; 21 | import org.hyperledger.fabric_ca.sdk.HFCAClient; 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | 25 | /** 26 | * 联盟组织对象 27 | * 28 | * @author 杨毅 29 | * @date 2017年9月7日 - 下午4:35:40 30 | * @email abericyang@gmail.com 31 | */ 32 | public class FabricOrg { 33 | 34 | private static Logger log = LoggerFactory.getLogger(FabricOrg.class); 35 | 36 | /** 37 | * 名称 38 | */ 39 | private String name; 40 | /** 41 | * 会员id 42 | */ 43 | private String mspid; 44 | /** 45 | * ca 客户端 46 | */ 47 | private HFCAClient caClient; 48 | 49 | /** 50 | * 用户集合 51 | */ 52 | Map userMap = new HashMap<>(); 53 | /** 54 | * 本地节点集合 55 | */ 56 | Map peerLocations = new HashMap<>(); 57 | /** 58 | * 本地排序服务集合 59 | */ 60 | Map ordererLocations = new HashMap<>(); 61 | /** 62 | * 本地事件集合 63 | */ 64 | Map eventHubLocations = new HashMap<>(); 65 | /** 66 | * 节点集合 67 | */ 68 | Set peers = new HashSet<>(); 69 | /** 70 | * 联盟管理员用户 71 | */ 72 | private FabricUser admin; 73 | /** 74 | * 本地 ca 75 | */ 76 | private String caLocation; 77 | /** 78 | * ca 配置 79 | */ 80 | private Properties caProperties = null; 81 | 82 | /** 83 | * 联盟单节点管理员用户 84 | */ 85 | private FabricUser peerAdmin; 86 | 87 | /** 88 | * 域名名称 89 | */ 90 | private String domainName; 91 | 92 | public FabricOrg(String username, Peers peers, Orderers orderers, FabricStore fabricStore, String cryptoConfigPath, boolean openCATLS) 93 | throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, IOException { 94 | this.name = peers.getOrgName(); 95 | this.mspid = peers.getOrgMSPID(); 96 | openCATLS=true; 97 | for (int i = 0; i < peers.get().size(); i++) { 98 | addPeerLocation(peers.get().get(i).getPeerName(), peers.get().get(i).getPeerLocation()); 99 | addEventHubLocation(peers.get().get(i).getPeerEventHubName(), peers.get().get(i).getPeerEventHubLocation()); 100 | setCALocation(peers.get().get(i).getCaLocation()); 101 | if (openCATLS) { 102 | // File caCert = Paths.get(cryptoConfigPath, "/peerOrganizations/", peers.getOrgDomainName(), String.format("/ca/ca.%s-cert.pem", peers.getOrgDomainName())).toFile(); 103 | File caCert = new File(cryptoConfigPath + "/peerOrganizations/" + peers.getOrgDomainName() + String.format("/ca/ca.%s-cert.pem", peers.getOrgDomainName())); 104 | if (!caCert.exists() || !caCert.isFile()) { 105 | throw new RuntimeException("TEST is missing cert file " + caCert.getAbsolutePath()); 106 | } 107 | Properties properties = new Properties(); 108 | properties.setProperty("pemFile", caCert.getAbsolutePath()); 109 | 110 | // properties.setProperty("allowAllHostNames", "true"); // 仅用于测试环境 111 | setCAProperties(properties); 112 | } 113 | } 114 | for (int i = 0; i < orderers.get().size(); i++) { 115 | addOrdererLocation(orderers.get().get(i).getOrdererName(), orderers.get().get(i).getOrdererLocation()); 116 | } 117 | setDomainName(peers.getOrgDomainName()); // domainName=tk.anti-moth.com 118 | 119 | 120 | File skFile = Paths.get(cryptoConfigPath, "/peerOrganizations/", peers.getOrgDomainName(), String.format("/users/%s@%s/msp/keystore", username, peers.getOrgDomainName())).toFile(); 121 | File certificateFile = Paths.get(cryptoConfigPath, "/peerOrganizations/", peers.getOrgDomainName(), 122 | String.format("/users/%s@%s/msp/signcerts/%s@%s-cert.pem", username, peers.getOrgDomainName(), username, peers.getOrgDomainName())).toFile(); 123 | log.debug("skFile = " + skFile.getAbsolutePath()); 124 | log.debug("certificateFile = " + certificateFile.getAbsolutePath()); 125 | setPeerAdmin(fabricStore.getMember(peers.getOrgName() + username, peers.getOrgName(), peers.getOrgMSPID(), findFileSk(skFile), certificateFile)); // 一个特殊的用户,可以创建通道,连接对等点,并安装链码 126 | } 127 | 128 | public String getName() { 129 | return name; 130 | } 131 | 132 | /** 133 | * 获取联盟管理员用户 134 | * 135 | * @return 联盟管理员用户 136 | */ 137 | public FabricUser getAdmin() { 138 | return admin; 139 | } 140 | 141 | /** 142 | * 设置联盟管理员用户 143 | * 144 | * @param admin 联盟管理员用户 145 | */ 146 | public void setAdmin(FabricUser admin) { 147 | this.admin = admin; 148 | } 149 | 150 | /** 151 | * 获取会员id 152 | * 153 | * @return 会员id 154 | */ 155 | public String getMSPID() { 156 | return mspid; 157 | } 158 | 159 | /** 160 | * 设置本地ca 161 | * 162 | * @param caLocation 本地ca 163 | */ 164 | public void setCALocation(String caLocation) { 165 | this.caLocation = caLocation; 166 | } 167 | 168 | /** 169 | * 获取本地ca 170 | * 171 | * @return 本地ca 172 | */ 173 | public String getCALocation() { 174 | return this.caLocation; 175 | } 176 | 177 | /** 178 | * 添加本地节点 179 | * 180 | * @param name 节点key 181 | * @param location 节点 182 | */ 183 | public void addPeerLocation(String name, String location) { 184 | peerLocations.put(name, location); 185 | } 186 | 187 | /** 188 | * 添加本地组织 189 | * 190 | * @param name 组织key 191 | * @param location 组织 192 | */ 193 | public void addOrdererLocation(String name, String location) { 194 | ordererLocations.put(name, location); 195 | } 196 | 197 | /** 198 | * 添加本地事件 199 | * 200 | * @param name 事件key 201 | * @param location 事件 202 | */ 203 | public void addEventHubLocation(String name, String location) { 204 | eventHubLocations.put(name, location); 205 | } 206 | 207 | /** 208 | * 获取本地节点 209 | * 210 | * @param name 节点key 211 | * @return 节点 212 | */ 213 | public String getPeerLocation(String name) { 214 | return peerLocations.get(name); 215 | } 216 | 217 | /** 218 | * 获取本地组织 219 | * 220 | * @param name 组织key 221 | * @return 组织 222 | */ 223 | public String getOrdererLocation(String name) { 224 | return ordererLocations.get(name); 225 | } 226 | 227 | /** 228 | * 获取本地事件 229 | * 230 | * @param name 事件key 231 | * @return 事件 232 | */ 233 | public String getEventHubLocation(String name) { 234 | return eventHubLocations.get(name); 235 | } 236 | 237 | /** 238 | * 获取一个不可修改的本地节点key集合 239 | * 240 | * @return 节点key集合 241 | */ 242 | public Set getPeerNames() { 243 | return Collections.unmodifiableSet(peerLocations.keySet()); 244 | } 245 | 246 | /** 247 | * 获取一个不可修改的本地节点集合 248 | * 249 | * @return 节点集合 250 | */ 251 | public Set getPeers() { 252 | return Collections.unmodifiableSet(peers); 253 | } 254 | 255 | /** 256 | * 获取一个不可修改的本地组织key集合 257 | * 258 | * @return 组织key集合 259 | */ 260 | public Set getOrdererNames() { 261 | return Collections.unmodifiableSet(ordererLocations.keySet()); 262 | } 263 | 264 | /** 265 | * 获取一个不可修改的本地组织集合 266 | * 267 | * @return 组织集合 268 | */ 269 | public Collection getOrdererLocations() { 270 | return Collections.unmodifiableCollection(ordererLocations.values()); 271 | } 272 | 273 | /** 274 | * 获取一个不可修改的本地事件key集合 275 | * 276 | * @return 事件key集合 277 | */ 278 | public Set getEventHubNames() { 279 | return Collections.unmodifiableSet(eventHubLocations.keySet()); 280 | } 281 | 282 | /** 283 | * 获取一个不可修改的本地事件集合 284 | * 285 | * @return 事件集合 286 | */ 287 | public Collection getEventHubLocations() { 288 | return Collections.unmodifiableCollection(eventHubLocations.values()); 289 | } 290 | 291 | /** 292 | * 设置 ca 客户端 293 | * 294 | * @param caClient ca 客户端 295 | */ 296 | public void setCAClient(HFCAClient caClient) { 297 | this.caClient = caClient; 298 | } 299 | 300 | /** 301 | * 获取 ca 客户端 302 | * 303 | * @return ca 客户端 304 | */ 305 | public HFCAClient getCAClient() { 306 | return caClient; 307 | } 308 | 309 | /** 310 | * 向用户集合中添加用户 311 | * 312 | * @param user 用户 313 | */ 314 | public void addUser(FabricUser user) { 315 | userMap.put(user.getName(), user); 316 | } 317 | 318 | /** 319 | * 从用户集合根据名称获取用户 320 | * 321 | * @param name 名称 322 | * @return 用户 323 | */ 324 | public User getUser(String name) { 325 | return userMap.get(name); 326 | } 327 | 328 | /** 329 | * 向节点集合中添加节点 330 | * 331 | * @param peer 节点 332 | */ 333 | public void addPeer(Peer peer) { 334 | peers.add(peer); 335 | } 336 | 337 | /** 338 | * 设置 ca 配置 339 | * 340 | * @param caProperties ca 配置 341 | */ 342 | public void setCAProperties(Properties caProperties) { 343 | this.caProperties = caProperties; 344 | } 345 | 346 | /** 347 | * 获取 ca 配置 348 | * 349 | * @return ca 配置 350 | */ 351 | public Properties getCAProperties() { 352 | return caProperties; 353 | } 354 | 355 | /** 356 | * 设置联盟单节点管理员用户 357 | * 358 | * @param peerAdmin 联盟单节点管理员用户 359 | */ 360 | public void setPeerAdmin(FabricUser peerAdmin) { 361 | this.peerAdmin = peerAdmin; 362 | } 363 | 364 | /** 365 | * 获取联盟单节点管理员用户 366 | * 367 | * @return 联盟单节点管理员用户 368 | */ 369 | public FabricUser getPeerAdmin() { 370 | return peerAdmin; 371 | } 372 | 373 | /** 374 | * 设置域名名称 375 | *

376 | * 域名名称 377 | */ 378 | public void setDomainName(String domainName) { 379 | this.domainName = domainName; 380 | } 381 | 382 | /** 383 | * 获取域名名称 384 | * 385 | * @return 域名名称 386 | */ 387 | public String getDomainName() { 388 | return domainName; 389 | } 390 | 391 | /** 392 | * 从指定路径中获取后缀为 _sk 的文件,且该路径下有且仅有该文件 393 | *

394 | * 指定路径 395 | * 396 | * @return File 397 | */ 398 | private File findFileSk(File directory) { 399 | File[] matches = directory.listFiles((dir, name) -> name.endsWith("_sk")); 400 | if (null == matches) { 401 | throw new RuntimeException(String.format("Matches returned null does %s directory exist?", directory.getAbsoluteFile().getName())); 402 | } 403 | if (matches.length != 1) { 404 | throw new RuntimeException(String.format("Expected in %s only 1 sk file but found %d", directory.getAbsoluteFile().getName(), matches.length)); 405 | } 406 | return matches[0]; 407 | } 408 | 409 | } 410 | -------------------------------------------------------------------------------- /blokinfo/src/App.vue: -------------------------------------------------------------------------------- 1 | 106 | 107 | 333 | 334 | 459 | -------------------------------------------------------------------------------- /sdk/src/main/java/cn/huse/trace/sdk/trace/ChaincodeManager.java: -------------------------------------------------------------------------------- 1 | package cn.huse.trace.sdk.trace; 2 | 3 | import cn.huse.trace.sdk.trace.bean.Chaincode; 4 | import cn.huse.trace.sdk.trace.bean.Orderers; 5 | import cn.huse.trace.sdk.trace.bean.Peers; 6 | import cn.huse.trace.web.common.QueryResult; 7 | import cn.huse.trace.web.response.model.BlockInfoModel; 8 | import com.google.protobuf.ByteString; 9 | import com.google.protobuf.InvalidProtocolBufferException; 10 | import org.apache.commons.codec.binary.Hex; 11 | import org.hyperledger.fabric.protos.ledger.rwset.kvrwset.KvRwset; 12 | import org.hyperledger.fabric.sdk.*; 13 | import org.hyperledger.fabric.sdk.BlockInfo.EnvelopeInfo; 14 | import org.hyperledger.fabric.sdk.BlockInfo.EnvelopeType; 15 | import org.hyperledger.fabric.sdk.BlockInfo.TransactionEnvelopeInfo; 16 | import org.hyperledger.fabric.sdk.exception.CryptoException; 17 | import org.hyperledger.fabric.sdk.exception.InvalidArgumentException; 18 | import org.hyperledger.fabric.sdk.exception.ProposalException; 19 | import org.hyperledger.fabric.sdk.exception.TransactionException; 20 | import org.hyperledger.fabric.sdk.security.CryptoSuite; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | import org.springframework.beans.factory.annotation.Value; 24 | import org.springframework.stereotype.Component; 25 | 26 | import javax.annotation.PostConstruct; 27 | import java.io.File; 28 | import java.io.IOException; 29 | import java.lang.reflect.InvocationTargetException; 30 | import java.nio.file.Paths; 31 | import java.security.NoSuchAlgorithmException; 32 | import java.security.NoSuchProviderException; 33 | import java.security.cert.CertificateException; 34 | import java.security.spec.InvalidKeySpecException; 35 | import java.util.*; 36 | import java.util.concurrent.ExecutionException; 37 | import java.util.concurrent.TimeoutException; 38 | 39 | import static java.nio.charset.StandardCharsets.UTF_8; 40 | 41 | @Component 42 | public class ChaincodeManager { 43 | private static Logger log = LoggerFactory.getLogger(ChaincodeManager.class); 44 | private FabricConfig config = new FabricConfig(); 45 | private Orderers orderers; 46 | private Peers peers; 47 | private Chaincode chaincode; 48 | private HFClient client; 49 | private FabricOrg fabricOrg; 50 | private Channel channel; 51 | private ChaincodeID chaincodeID; 52 | @Value("${fabric.path}") 53 | private String path; 54 | 55 | public String getPath() { 56 | return path; 57 | } 58 | 59 | public void setPath(String path) { 60 | this.path = path; 61 | } 62 | 63 | @PostConstruct 64 | public void init() { 65 | try { 66 | config.setDirectory(path); 67 | FabricManager.obtain(config); 68 | } catch (CryptoException | InvalidArgumentException | NoSuchAlgorithmException | NoSuchProviderException | InvalidKeySpecException | TransactionException | IOException e) { 69 | e.printStackTrace(); 70 | log.error("obtain fabricManager failed." + e.getMessage()); 71 | } 72 | } 73 | 74 | public ChaincodeManager() { 75 | } 76 | 77 | public ChaincodeManager(String username, FabricConfig fabricConfig) 78 | throws CryptoException, InvalidArgumentException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, IOException, TransactionException { 79 | this.config = fabricConfig; 80 | 81 | orderers = this.config.getOrderers(); 82 | peers = this.config.getPeers(); 83 | chaincode = this.config.getChaincode(); 84 | 85 | client = HFClient.createNewInstance(); 86 | log.debug("Create instance of HFClient"); 87 | try { 88 | client.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite()); 89 | } catch (IllegalAccessException e) { 90 | e.printStackTrace(); 91 | } catch (InstantiationException e) { 92 | e.printStackTrace(); 93 | } catch (ClassNotFoundException e) { 94 | e.printStackTrace(); 95 | } catch (NoSuchMethodException e) { 96 | e.printStackTrace(); 97 | } catch (InvocationTargetException e) { 98 | e.printStackTrace(); 99 | } 100 | log.debug("Set Crypto Suite of HFClient"); 101 | 102 | fabricOrg = getFabricOrg(username, config.openCATLS()); 103 | channel = getChannel(); 104 | chaincodeID = getChaincodeID(); 105 | 106 | client.setUserContext(fabricOrg.getPeerAdmin()); 107 | } 108 | 109 | private FabricOrg getFabricOrg(String username, boolean openCATLS) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, IOException { 110 | 111 | // java.io.tmpdir : C:\Users\yangyi47\AppData\Local\Temp\ 112 | File storeFile = new File(System.getProperty("java.io.tmpdir") + "/HFCSampletest.properties"); 113 | FabricStore fabricStore = new FabricStore(storeFile); 114 | 115 | // Get Org1 from configuration 116 | FabricOrg fabricOrg = new FabricOrg(username, peers, orderers, fabricStore, config.getCryptoConfigPath(), openCATLS); 117 | log.debug("Get FabricOrg"); 118 | return fabricOrg; 119 | } 120 | 121 | private Channel getChannel() 122 | throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, IOException, CryptoException, InvalidArgumentException, TransactionException { 123 | client.setUserContext(fabricOrg.getPeerAdmin()); 124 | return getChannel(fabricOrg, client); 125 | } 126 | 127 | public Channel getChannelInstant() { 128 | return channel; 129 | } 130 | 131 | private Channel getChannel(FabricOrg fabricOrg, HFClient client) 132 | throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, IOException, CryptoException, InvalidArgumentException, TransactionException { 133 | Channel channel = client.newChannel(chaincode.getChannelName()); 134 | log.debug("Get Chain " + chaincode.getChannelName()); 135 | 136 | // channel.setTransactionWaitTime(chaincode.getTransactionWaitTime()); 137 | // channel.setDeployWaitTime(chaincode.getDeployWaitTime()); 138 | 139 | for (int i = 0; i < peers.get().size(); i++) { 140 | File peerCert = Paths.get(config.getCryptoConfigPath(), "/peerOrganizations", peers.getOrgDomainName(), "peers", peers.get().get(i).getPeerName(), "tls/server.crt") 141 | .toFile(); 142 | if (!peerCert.exists()) { 143 | throw new RuntimeException( 144 | String.format("Missing cert file for: %s. Could not find at location: %s", peers.get().get(i).getPeerName(), peerCert.getAbsolutePath())); 145 | } 146 | Properties peerProperties = new Properties(); 147 | peerProperties.setProperty("pemFile", peerCert.getAbsolutePath()); 148 | peerProperties.setProperty("trustServerCertificate", "true"); 149 | peerProperties.setProperty("hostnameOverride", peers.getOrgDomainName()); 150 | peerProperties.setProperty("sslProvider", "openSSL"); 151 | peerProperties.setProperty("negotiationType", "TLS"); 152 | // 在grpc的NettyChannelBuilder上设置特定选项 153 | peerProperties.put("grpc.ManagedChannelBuilderOption.maxInboundMessageSize", 9000000); 154 | channel.addPeer(client.newPeer(peers.get().get(i).getPeerName(), fabricOrg.getPeerLocation(peers.get().get(i).getPeerName()), peerProperties)); 155 | if (peers.get().get(i).isAddEventHub()) { 156 | channel.addEventHub(client.newEventHub(peers.get().get(i).getPeerEventHubName(), fabricOrg.getEventHubLocation(peers.get().get(i).getPeerEventHubName()), peerProperties)); 157 | } 158 | } 159 | 160 | for (int i = 0; i < orderers.get().size(); i++) { 161 | File ordererCert = Paths.get(config.getCryptoConfigPath(), "/ordererOrganizations", orderers.getOrdererDomainName(), "orderers", orderers.get().get(i).getOrdererName(), 162 | "tls/server.crt").toFile(); 163 | if (!ordererCert.exists()) { 164 | throw new RuntimeException( 165 | String.format("Missing cert file for: %s. Could not find at location: %s", orderers.get().get(i).getOrdererName(), ordererCert.getAbsolutePath())); 166 | } 167 | Properties ordererProperties = new Properties(); 168 | ordererProperties.setProperty("pemFile", ordererCert.getAbsolutePath()); 169 | ordererProperties.setProperty("hostnameOverride", orderers.getOrdererDomainName()); 170 | ordererProperties.setProperty("sslProvider", "openSSL"); 171 | ordererProperties.setProperty("negotiationType", "TLS"); 172 | ordererProperties.put("grpc.ManagedChannelBuilderOption.maxInboundMessageSize", 9000000); 173 | ordererProperties.setProperty("ordererWaitTimeMilliSecs", "300000"); 174 | channel.addOrderer(client.newOrderer(orderers.get().get(i).getOrdererName(), fabricOrg.getOrdererLocation(orderers.get().get(i).getOrdererName()), ordererProperties)); 175 | } 176 | log.debug("channel.isInitialized = " + channel.isInitialized()); 177 | if (!channel.isInitialized()) { 178 | channel.initialize(); 179 | } 180 | if (config.isRegisterEvent()) { 181 | log.debug("========================Event事件监听注册========================"); 182 | channel.registerBlockListener(new BlockListener() { 183 | 184 | @Override 185 | public void received(BlockEvent event) { 186 | // TODO 187 | log.debug("========================Event事件监听开始========================"); 188 | try { 189 | log.debug("event.getChannelId() = " + event.getChannelId()); 190 | // log.debug("event.getEvent().getChaincodeEvent().getPayload().toStringUtf8() = " + event.getEvent().getChaincodeEvent().getPayload().toStringUtf8()); 191 | log.debug("event.getBlock().getData().getDataList().size() = " + event.getBlock().getData().getDataList().size()); 192 | ByteString byteString = event.getBlock().getData().getData(0); 193 | String result = byteString.toStringUtf8(); 194 | log.debug("byteString.toStringUtf8() = " + result); 195 | 196 | String r1[] = result.split("END CERTIFICATE"); 197 | String rr = r1[2]; 198 | log.debug("rr = " + rr); 199 | } catch (InvalidProtocolBufferException e) { 200 | // TODO 201 | e.printStackTrace(); 202 | } 203 | log.debug("========================Event事件监听结束========================"); 204 | } 205 | }); 206 | } 207 | return channel; 208 | } 209 | 210 | private ChaincodeID getChaincodeID() { 211 | return ChaincodeID.newBuilder().setName(chaincode.getChaincodeName()).setVersion(chaincode.getChaincodeVersion()).setPath(chaincode.getChaincodePath()).build(); 212 | } 213 | 214 | /** 215 | * 执行智能合约 216 | * 217 | * @param fcn 方法名 218 | * @param args 参数数组 219 | * @return 220 | * @throws InvalidArgumentException 221 | * @throws ProposalException 222 | * @throws InterruptedException 223 | * @throws ExecutionException 224 | * @throws TimeoutException 225 | * @throws IOException 226 | * @throws TransactionException 227 | * @throws CryptoException 228 | * @throws InvalidKeySpecException 229 | * @throws NoSuchProviderException 230 | * @throws NoSuchAlgorithmException 231 | */ 232 | public QueryResult invoke(String fcn, String[] args) 233 | throws InvalidArgumentException, ProposalException, InterruptedException, ExecutionException, TimeoutException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, CryptoException, TransactionException, IOException { 234 | QueryResult queryResult = new QueryResult(); 235 | Collection successful = new LinkedList<>(); 236 | Collection failed = new LinkedList<>(); 237 | /// Send transaction proposal to all peers 238 | TransactionProposalRequest transactionProposalRequest = client.newTransactionProposalRequest(); 239 | transactionProposalRequest.setChaincodeID(chaincodeID); 240 | transactionProposalRequest.setFcn(fcn); 241 | transactionProposalRequest.setArgs(args); 242 | Map tm2 = new HashMap<>(); 243 | tm2.put("HyperLedgerFabric", "TransactionProposalRequest:JavaSDK".getBytes(UTF_8)); 244 | tm2.put("method", "TransactionProposalRequest".getBytes(UTF_8)); 245 | tm2.put("result", ":)".getBytes(UTF_8)); 246 | transactionProposalRequest.setTransientMap(tm2); 247 | 248 | long currentStart = System.currentTimeMillis(); 249 | Collection transactionPropResp = channel.sendTransactionProposal(transactionProposalRequest, channel.getPeers()); 250 | for (ProposalResponse response : transactionPropResp) { 251 | if (response.getStatus() == ProposalResponse.Status.SUCCESS) { 252 | successful.add(response); 253 | } else { 254 | failed.add(response); 255 | } 256 | } 257 | Collection> proposalConsistencySets = SDKUtils.getProposalConsistencySets(transactionPropResp); 258 | if (proposalConsistencySets.size() != 1) { 259 | log.error("Expected only one set of consistent proposal responses but got " + proposalConsistencySets.size()); 260 | } 261 | 262 | if (failed.size() > 0) { 263 | ProposalResponse firstTransactionProposalResponse = failed.iterator().next(); 264 | log.error("Not enough endorsers for inspect:" + failed.size() + " endorser error: " + firstTransactionProposalResponse.getMessage() + ". Was verified: " 265 | + firstTransactionProposalResponse.isVerified()); 266 | queryResult.setCode(QueryResult.CODE_ERROR); 267 | queryResult.setData(firstTransactionProposalResponse.getMessage()); 268 | return queryResult; 269 | } else { 270 | log.info("Successfully received transaction proposal responses."); 271 | ProposalResponse resp = transactionPropResp.iterator().next(); 272 | log.debug("TransactionID: " + resp.getTransactionID()); 273 | byte[] x = resp.getChaincodeActionResponsePayload(); 274 | String resultAsString = null; 275 | if (x != null) { 276 | resultAsString = new String(x, "UTF-8"); 277 | } 278 | log.info("resultAsString = " + resultAsString); 279 | channel.sendTransaction(successful); 280 | queryResult.setCode(QueryResult.CODE_SUCCESS); 281 | queryResult.setData(resultAsString); 282 | queryResult.setTxId(resp.getTransactionID()); 283 | return queryResult; 284 | } 285 | 286 | // channel.sendTransaction(successful).thenApply(transactionEvent -> { 287 | // if (transactionEvent.isValid()) { 288 | // log.info("Successfully send transaction proposal to orderer. Transaction ID: " + transactionEvent.getTransactionID()); 289 | // } else { 290 | // log.info("Failed to send transaction proposal to orderer"); 291 | // } 292 | // // chain.shutdown(true); 293 | // return transactionEvent.getTransactionID(); 294 | // }).get(chaincode.getInvokeWatiTime(), TimeUnit.SECONDS); 295 | } 296 | 297 | /** 298 | * 查询智能合约 299 | * 300 | * @param fcn 方法名 301 | * @param args 参数数组 302 | * @return 303 | * @throws InvalidArgumentException 304 | * @throws ProposalException 305 | * @throws IOException 306 | * @throws TransactionException 307 | * @throws CryptoException 308 | * @throws InvalidKeySpecException 309 | * @throws NoSuchProviderException 310 | * @throws NoSuchAlgorithmException 311 | */ 312 | public Map query(String fcn, String[] args) throws InvalidArgumentException, ProposalException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, CryptoException, TransactionException, IOException { 313 | Map resultMap = new HashMap<>(); 314 | String payload = ""; 315 | QueryByChaincodeRequest queryByChaincodeRequest = client.newQueryProposalRequest(); 316 | queryByChaincodeRequest.setArgs(args); 317 | queryByChaincodeRequest.setFcn(fcn); 318 | queryByChaincodeRequest.setChaincodeID(chaincodeID); 319 | 320 | Map tm2 = new HashMap<>(); 321 | tm2.put("HyperLedgerFabric", "QueryByChaincodeRequest:JavaSDK".getBytes(UTF_8)); 322 | tm2.put("method", "QueryByChaincodeRequest".getBytes(UTF_8)); 323 | queryByChaincodeRequest.setTransientMap(tm2); 324 | 325 | Collection queryProposals = channel.queryByChaincode(queryByChaincodeRequest, channel.getPeers()); 326 | for (ProposalResponse proposalResponse : queryProposals) { 327 | if (!proposalResponse.isVerified() || proposalResponse.getStatus() != ProposalResponse.Status.SUCCESS) { 328 | log.debug("Failed query proposal from peer " + proposalResponse.getPeer().getName() + " status: " + proposalResponse.getStatus() + ". Messages: " 329 | + proposalResponse.getMessage() + ". Was verified : " + proposalResponse.isVerified()); 330 | resultMap.put("code", "error"); 331 | resultMap.put("data", "Failed query proposal from peer " + proposalResponse.getPeer().getName() + " status: " + proposalResponse.getStatus() + ". Messages: " 332 | + proposalResponse.getMessage() + ". Was verified : " + proposalResponse.isVerified()); 333 | } else { 334 | payload = proposalResponse.getProposalResponse().getResponse().getPayload().toStringUtf8(); 335 | log.debug("Query payload from peer: " + proposalResponse.getPeer().getName()); 336 | log.debug("TransactionID: " + proposalResponse.getTransactionID()); 337 | log.debug("" + payload); 338 | resultMap.put("code", "success"); 339 | resultMap.put("data", ChaincodeManager.printableString(payload)); 340 | resultMap.put("txid", proposalResponse.getTransactionID()); 341 | } 342 | } 343 | return resultMap; 344 | } 345 | 346 | public BlockInfoModel queryBlockByTransactionID(String txID) throws InvalidArgumentException, ProposalException, CertificateException, IOException { 347 | BlockInfo blockInfo = channel.queryBlockByTransactionID(txID); 348 | execBlockInfo(blockInfo); 349 | return null; 350 | } 351 | 352 | public BlockInfoModel queryBlockByHash(byte[] blockHash) throws InvalidArgumentException, ProposalException, IOException { 353 | BlockInfo blockInfo = channel.queryBlockByHash(blockHash); 354 | // execBlockInfo(blockInfo); 355 | return new BlockInfoModel(blockInfo); 356 | } 357 | 358 | public BlockInfoModel queryBlockByNumber(long blockNumber) throws InvalidArgumentException, ProposalException, IOException { 359 | BlockInfo blockInfo = channel.queryBlockByNumber(blockNumber); 360 | return new BlockInfoModel(blockInfo); 361 | } 362 | 363 | private void execBlockInfo(BlockInfo blockInfo) throws InvalidArgumentException, IOException { 364 | final long blockNumber = blockInfo.getBlockNumber(); 365 | log.debug("blockNumber = " + blockNumber); 366 | log.debug("data hash: " + Hex.encodeHexString(blockInfo.getDataHash())); 367 | log.debug("previous hash id: " + Hex.encodeHexString(blockInfo.getPreviousHash())); 368 | // log.debug("calculated block hash is " + Hex.encodeHexString(SDKUtils.calculateBlockHash(blockNumber, blockInfo.getPreviousHash(), blockInfo.getDataHash()))); 369 | 370 | final int envelopeCount = blockInfo.getEnvelopeCount(); 371 | log.debug("block number " + blockNumber + " has " + envelopeCount + " envelope count:"); 372 | 373 | for (EnvelopeInfo info : blockInfo.getEnvelopeInfos()) { 374 | final String channelId = info.getChannelId(); 375 | log.debug("ChannelId = " + channelId); 376 | log.debug("Epoch = " + info.getEpoch()); 377 | log.debug("TransactionID = " + info.getTransactionID()); 378 | log.debug("ValidationCode = " + info.getValidationCode()); 379 | // log.debug("Timestamp = " + DateUtil.obtain().parseDateFormat(new Date(info.getTimestamp().getTime()), "yyyy年MM月dd日 HH时mm分ss秒")); 380 | log.debug("Type = " + info.getType()); 381 | 382 | if (info.getType() == EnvelopeType.TRANSACTION_ENVELOPE) { 383 | TransactionEnvelopeInfo txeInfo = (TransactionEnvelopeInfo) info; 384 | int txCount = txeInfo.getTransactionActionInfoCount(); 385 | log.debug("Transaction number " + blockNumber + " has actions count = " + txCount); 386 | log.debug("Transaction number " + blockNumber + " isValid = " + txeInfo.isValid()); 387 | log.debug("Transaction number " + blockNumber + " validation code = " + txeInfo.getValidationCode()); 388 | 389 | for (int i = 0; i < txCount; i++) { 390 | TransactionEnvelopeInfo.TransactionActionInfo txInfo = txeInfo.getTransactionActionInfo(i); 391 | log.debug("Transaction action " + i + " has response status " + txInfo.getResponseStatus()); 392 | log.debug("Transaction action " + i + " has response message bytes as string: " + printableString(new String(txInfo.getResponseMessageBytes(), "UTF-8"))); 393 | log.debug("Transaction action " + i + " has endorsements " + txInfo.getEndorsementsCount()); 394 | 395 | for (int n = 0; n < txInfo.getEndorsementsCount(); ++n) { 396 | BlockInfo.EndorserInfo endorserInfo = txInfo.getEndorsementInfo(n); 397 | log.debug("Endorser " + n + " signature: " + Hex.encodeHexString(endorserInfo.getSignature())); 398 | log.debug("Endorser " + n + " endorser: " + new String(endorserInfo.getEndorser(), "UTF-8")); 399 | } 400 | 401 | log.debug("Transaction action " + i + " has " + txInfo.getChaincodeInputArgsCount() + " chaincode input arguments"); 402 | for (int z = 0; z < txInfo.getChaincodeInputArgsCount(); ++z) { 403 | log.debug("Transaction action " + i + " has chaincode input argument " + z + "is: " + printableString(new String(txInfo.getChaincodeInputArgs(z), "UTF-8"))); 404 | } 405 | 406 | log.debug("Transaction action " + i + " proposal response status: " + txInfo.getProposalResponseStatus()); 407 | log.debug("Transaction action " + i + " proposal response payload: " + printableString(new String(txInfo.getProposalResponsePayload()))); 408 | 409 | TxReadWriteSetInfo rwsetInfo = txInfo.getTxReadWriteSet(); 410 | if (null != rwsetInfo) { 411 | log.debug("Transaction action " + i + " has " + rwsetInfo.getNsRwsetCount() + " name space read write sets"); 412 | 413 | for (TxReadWriteSetInfo.NsRwsetInfo nsRwsetInfo : rwsetInfo.getNsRwsetInfos()) { 414 | final String namespace = nsRwsetInfo.getNamespace(); 415 | KvRwset.KVRWSet rws = nsRwsetInfo.getRwset(); 416 | int rs = -1; 417 | for (KvRwset.KVRead readList : rws.getReadsList()) { 418 | rs++; 419 | log.debug("Namespace " + namespace + " read set " + rs + " key " + readList.getKey() + " version [" + readList.getVersion().getBlockNum() + " : " + readList.getVersion().getTxNum() + "]"); 420 | } 421 | 422 | rs = -1; 423 | for (KvRwset.KVWrite writeList : rws.getWritesList()) { 424 | rs++; 425 | String valAsString = printableString(new String(writeList.getValue().toByteArray(), "UTF-8")); 426 | log.debug("Namespace " + namespace + " write set " + rs + " key " + writeList.getKey() + " has value " + valAsString); 427 | } 428 | } 429 | } 430 | } 431 | } 432 | } 433 | } 434 | 435 | public static String printableString(final String string) { 436 | return string; 437 | /* int maxLogStringLength = 64; 438 | if (string == null || string.length() == 0) { 439 | return string; 440 | } 441 | String ret = string.replaceAll("[^\\p{Print}]", "?"); 442 | ret = ret.substring(0, Math.min(ret.length(), maxLogStringLength)) + (ret.length() > maxLogStringLength ? "..." : ""); 443 | return ret;*/ 444 | } 445 | 446 | public FabricConfig getConfig() { 447 | return config; 448 | } 449 | } 450 | --------------------------------------------------------------------------------