api.dart 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. import 'dart:async';
  2. import 'dart:math';
  3. import 'dart:typed_data';
  4. import 'package:f_cache/manager.dart';
  5. import 'package:grpc/grpc.dart';
  6. import 'package:fixnum/fixnum.dart' as fixnum;
  7. import 'package:trackoffical_app/model.dart';
  8. import 'package:trackoffical_app/service/app.dart';
  9. import 'package:trackoffical_app/pb.dart' as pb;
  10. import 'package:trackoffical_app/service/service.dart';
  11. import 'package:trackoffical_app/utils.dart';
  12. import 'package:protobuf/protobuf.dart';
  13. import '../logger.dart';
  14. import '../global.dart';
  15. class _Stub{
  16. _Stub(
  17. this.channel,
  18. this.stub
  19. );
  20. ClientChannel channel;
  21. pb.ApiToAppClient stub;
  22. }
  23. class ApiService extends IService {
  24. static ApiService get to => Get.find();
  25. ClientChannel? channel;
  26. String? get token {
  27. final out = App.to.userProfile.token.val;
  28. if (out.isEmpty) {
  29. return null;
  30. }
  31. return out;
  32. }
  33. set token(String? v) {
  34. App.to.userProfile.token.val = v ?? '';
  35. }
  36. String get _appVersion => App.to.appVersion;
  37. ClientChannel _newChannel(){
  38. return ClientChannel(
  39. GlobalVar.apiHost,
  40. port: GlobalVar.apiPort,
  41. options: const ChannelOptions(credentials:
  42. // ChannelCredentials.secure()
  43. ChannelCredentials.insecure()
  44. ),
  45. );
  46. }
  47. pb.ApiToAppClient _getStub({Duration? timeout, ClientChannel? channel}){
  48. if (this.channel == null) {
  49. throw Exception('$runtimeType 未初始化');
  50. }
  51. final metadata = <String, String>{
  52. 'source': "${pb.LoginSource.ToApp.value}"
  53. };
  54. metadata['version'] = _appVersion;
  55. if (token != null) {
  56. metadata['token'] = token!;
  57. }
  58. info("token: $token");
  59. return pb.ApiToAppClient(channel??this.channel!,
  60. options: CallOptions(
  61. metadata: metadata,
  62. timeout: timeout,
  63. ));
  64. }
  65. pb.ApiToAppClient get stub {
  66. return _getStub(timeout: 10.seconds);
  67. }
  68. _Stub get _stubForStream {
  69. final ch = _newChannel();
  70. return _Stub(ch, _getStub(channel: ch)) ;
  71. }
  72. Future<void> syncTime()async{
  73. try {
  74. final serverNow = await serverTime();
  75. App.to.correctByServerNow(serverNow);
  76. info('服务器时间:${App.to.now}');
  77. } catch(e){
  78. warn("获取服务器时间失败: ", e);
  79. }
  80. }
  81. @override
  82. Future<ApiService> init() async {
  83. channel = _newChannel();
  84. syncTime();
  85. return this;
  86. }
  87. Future<bool> isSignIn() async {
  88. final token = this.token;
  89. if (token == null) {
  90. return false;
  91. }
  92. if (token.isNotEmpty) {
  93. // try {
  94. // await flushUserInfo();
  95. // } catch (e) {
  96. // return false;
  97. // }
  98. return true;
  99. } else {
  100. return false;
  101. }
  102. }
  103. // Future<void> getVfCode() async {
  104. // await stub.getVfPic(pb.DefaultRequest());
  105. // }
  106. // 获取短信验证码
  107. Future<void> authSendCodeToPhone(String phone, pb.SmsType smsType)async{
  108. info('authSendCodeToPhone [$phone]');
  109. await stub.toSendCodeToPhoneV2(pb.ToSendCodeToPhoneRequestV2()
  110. ..phone= phone
  111. ..smsType= smsType
  112. );
  113. }
  114. // 场控端_登录
  115. Future<void> signIn(String userCode, String password, String ip) async {
  116. final r = await stub.toSignInV2(pb.ToSignInRequestV2()
  117. ..userCode = userCode
  118. ..password = password
  119. ..ip = ip
  120. );
  121. token = r.token;
  122. debug('sign in success: $token');
  123. }
  124. // 场控端_登出
  125. void signOut(){
  126. stub.toSignOutV2(pb.DefaultRequest());
  127. token = null;
  128. }
  129. @override
  130. void onClose() {
  131. channel?.shutdown();
  132. }
  133. // 获取系统时间
  134. Future<DateTime> serverTime() async {
  135. final begin = DateTime.now();
  136. final r = await stub.toGetServerTime(pb.DefaultRequest());
  137. final cost = DateTime.now().difference(begin);
  138. final serverNow = DateTime.fromMillisecondsSinceEpoch(
  139. r.millisecondStamp.toInt(),
  140. isUtc: true)
  141. .toLocal();
  142. return serverNow.add(cost);
  143. }
  144. Future<Duration> getSmsSendLeftTime(String phone)async{
  145. final r = await stub.toGetSmsSendLeftTimeV2(pb.GetSmsSendLeftTimeRequest()..phone= phone);
  146. info('getSmsSendLeftTime: $phone - ${r.second}s');
  147. return r.second.seconds;
  148. }
  149. // Future<pb.GetUpdateVersionReply> getUpdateVersion(String version)async{
  150. // info('getUpdateVersion: $version');
  151. // final r = await stub.getUpdateVersion(pb.GetUpdateVersionRequest()..vCode= version);
  152. // return r;
  153. // }
  154. // 场控端_地图列表
  155. Future<List<MapInfo>> mapList(MPosition? position, int offset, int limit) async{
  156. info('mapList: $position');
  157. final req = pb.MapListRequestV2()
  158. ..offset = offset
  159. ..limit = limit;
  160. if (position != null){
  161. req.position = position.toPb();
  162. }
  163. final r = await stub.toMapListV2(req);
  164. return r.list.map((e) => e.toModel()).toList();
  165. }
  166. // 场控端_地图自身信息
  167. Future<pb.ToMapInfoV2> mapDetail(int mapId, String pin)async{
  168. info('mapDetail mapId: $mapId pin: $pin');
  169. final r = await stub.toMapDetailV2(pb.IdRequest()
  170. ..id = fixnum.Int64(mapId)
  171. );
  172. return r;
  173. }
  174. // 场控端_地图内正在进行中的活动人员详情
  175. Future<pb.ToUserDetailQueryReplyV2> userDetailQuery(int mapId, bool isFullQuery)async{
  176. info('userDetailQuery mapId: $mapId isFullQuery: $isFullQuery');
  177. final r = await stub.toUserDetailQueryV2(pb.ToUserDetailQueryRequestV2()
  178. // ..mapId = fixnum.Int64(mapId)
  179. ..mapId = mapId
  180. ..isFullQuery = isFullQuery
  181. );
  182. return r;
  183. }
  184. // Future<Bin> getBinByMd5(Uint8List md5, OnPercentage onPercentage) async{
  185. // final stream = _getStub().toGetBinaryByMd5(pb.ToGetBinaryByMd5Request()..md5=md5);
  186. // String ext='';
  187. // Uint8List? data;
  188. // var nonce=Uint8List(0);
  189. // var i = 0;
  190. //
  191. // await for (var one in stream){
  192. // if(data == null){
  193. // data = Uint8List(one.allCount);
  194. // ext = one.ext;
  195. // nonce= Uint8List.fromList(one.nonce);
  196. // }
  197. //
  198. // for(var b in one.data){
  199. // data[i]=b;
  200. // i++;
  201. // }
  202. //
  203. // onPercentage(i, data.length);
  204. // }
  205. //
  206. //
  207. // await stream.cancel();
  208. // return Bin()
  209. // ..ext=ext
  210. // ..data=data??Uint8List(0)
  211. // ..nonce=nonce;
  212. // return Bin();
  213. // }
  214. Future<BinReader> getBinReaderByMd5(Uint8List md5) async {
  215. final stream = _getStub().toGetBinaryByMd5(pb.ToGetBinaryByMd5Request()..md5=md5);
  216. final controller = StreamController<List<int>>();
  217. controller.onCancel = (){
  218. stream.cancel();
  219. };
  220. Future<void> rcv()async{
  221. try{
  222. await for(final one in stream){
  223. controller.add(one.data);
  224. }
  225. }finally{
  226. controller.close();
  227. stream.cancel();
  228. }
  229. }
  230. rcv();
  231. stream.headers.then((value) => debug(value));
  232. final headers = await stream.headers;
  233. final lenStr = headers['all-length']!;
  234. final length = int.parse(lenStr);
  235. final nonce = headers['nonce']!;
  236. final ext = headers['ext']!;
  237. return BinReader(Reader( controller.stream, length))
  238. ..ext=ext
  239. ..nonce=nonce
  240. ;
  241. }
  242. }
  243. // class Bin{
  244. // var ext='';
  245. // var data = Uint8List(0);
  246. // var nonce = Uint8List(0);
  247. // }
  248. class BinReader{
  249. BinReader(this.reader);
  250. var ext='';
  251. Reader reader;
  252. var nonce='';
  253. }
  254. // typedef OnPercentage = void Function(int, int);
  255. class ApiServiceMock extends ApiService {
  256. @override
  257. Future<ApiService> init() async {
  258. debug('$runtimeType ready!');
  259. return this;
  260. }
  261. final random = Random();
  262. @override
  263. Future<void> signIn(String userCode, String password, String ip) async {}
  264. @override
  265. void onReady() {}
  266. @override
  267. void onClose() {}
  268. @override
  269. Future<DateTime> serverTime() async {
  270. return DateTime.now();
  271. }
  272. /*
  273. @override
  274. Future<pb.MapActivityListReply> mapActivityList(int mapId, String pin)async{
  275. return pb.MapActivityListReply()
  276. ..list.addAll([
  277. pb.MapActivitySimpleInfo()
  278. ..id=0
  279. ..name= '穿越荒野:勇闯野性之旅-穿越荒野:勇闯野性之旅-穿越荒野:勇闯野性之旅-穿越荒野:勇闯野性之旅-穿越荒野:勇闯野性之旅'
  280. ..difficulty= 1
  281. ..distanceMinMeter= 217.1
  282. ..closeDoorTime= 190.minutes.toPb()
  283. ..isUsed= false
  284. ..routeCount= 28
  285. ..totalControlNum= 12
  286. ,
  287. pb.MapActivitySimpleInfo()
  288. ..id=1
  289. ..name= '极限挑战 战胜重力'
  290. ..difficulty= 2
  291. ..distanceMinMeter= 8.1
  292. ..closeDoorTime= 190.minutes.toPb()
  293. ..isUsed= true
  294. ..routeCount= 6
  295. ..totalControlNum= 16
  296. ,
  297. ])
  298. ;
  299. }
  300. @override
  301. Future<pb.ActivityDetailReply> activityDetail(int id) async {
  302. final simple = pb.MapActivitySimpleInfo()
  303. ..id= 1
  304. ..name= '周末爬山健身运动'
  305. ..closeDoorTime= 90.minutes.toPb()
  306. ..distanceMinMeter= 71.3;
  307. return pb.ActivityDetailReply()
  308. ..baseInfoV2= simple
  309. ..content= ' 此主题为全民爬山健身定向运动,全民爬山健身运动是我国体育事业发展的重要组成部分,也是实现中华民族伟大复兴中国梦的重要内容。'
  310. ..routes.addAll([
  311. pb.MapRoute()
  312. ..name='XL0002'
  313. ..distanceMeter= 3331
  314. ..altitudeDiffMeter= 223
  315. ..useCount= 0
  316. ,
  317. pb.MapRoute()
  318. ..name= 'XL0003'
  319. ..distanceMeter= 5430
  320. ..altitudeDiffMeter= 123
  321. ..useCount=2
  322. ,
  323. ])
  324. ;
  325. }
  326. */
  327. // @override
  328. Future<List<MapInfo>> mapRecommendList(MPosition? position)async{
  329. info('mapRecommendList: $position');
  330. return [
  331. MapInfo()
  332. ..name= '英雄山风景区'
  333. // ..isOpen=true
  334. // ..isRecommend=true
  335. ,
  336. MapInfo()..name= '板桥广场'
  337. // ..isRecommend=true
  338. ];
  339. }
  340. @override
  341. Future<List<MapInfo>> mapList(MPosition? position, int offset, int limit)async{
  342. info('mapList: $position');
  343. final out = <MapInfo>[];
  344. for(var i=0;i< limit;i++){
  345. out.add(MapInfo()
  346. ..name ='$offset-${random.nextInt(10000)}公园'
  347. );
  348. }
  349. return out;
  350. }
  351. }
  352. /*
  353. class StreamGuardianWatch{
  354. StreamGuardianWatch(
  355. this.channel,
  356. this.stream
  357. );
  358. ClientChannel channel;
  359. ResponseStream<pb.PupilInGameWatchReply> stream;
  360. }
  361. */
  362. extension ExtInt64 on fixnum.Int64 {
  363. pb.IdRequest toIdRequest(){
  364. return pb.IdRequest()
  365. ..id = this;
  366. }
  367. }
  368. extension ExtNum on num {
  369. pb.IdRequest toIdRequest(){
  370. return pb.IdRequest()
  371. ..id = fixnum.Int64(toInt());
  372. }
  373. }