wip: me panel.
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import 'package:angel3_orm/angel3_orm.dart';
|
||||
import 'package:angel3_serialize/angel3_serialize.dart';
|
||||
import 'package:dde_gesture_manager_api/src/models/base_model.dart';
|
||||
import 'package:angel3_migration/angel3_migration.dart';
|
||||
import 'package:optional/optional.dart';
|
||||
|
||||
part 'download_history.g.dart';
|
||||
|
||||
@serializable
|
||||
@orm
|
||||
abstract class _DownloadHistory extends BaseModel {
|
||||
@Column(isNullable: false, indexType: IndexType.standardIndex)
|
||||
@SerializableField(isNullable: false)
|
||||
int? get uid;
|
||||
|
||||
@Column(isNullable: false, indexType: IndexType.standardIndex)
|
||||
@SerializableField(isNullable: false)
|
||||
int? get schemeId;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'package:angel3_orm/angel3_orm.dart';
|
||||
import 'package:angel3_serialize/angel3_serialize.dart';
|
||||
import 'package:dde_gesture_manager_api/src/models/base_model.dart';
|
||||
import 'package:angel3_migration/angel3_migration.dart';
|
||||
import 'package:optional/optional.dart';
|
||||
|
||||
part 'like_record.g.dart';
|
||||
|
||||
@serializable
|
||||
@orm
|
||||
abstract class _LikeRecord extends BaseModel {
|
||||
@Column(isNullable: false, indexType: IndexType.standardIndex)
|
||||
@SerializableField(isNullable: false)
|
||||
int? get uid;
|
||||
|
||||
@Column(isNullable: false, indexType: IndexType.standardIndex)
|
||||
@SerializableField(isNullable: false)
|
||||
int? get schemeId;
|
||||
|
||||
@Column(isNullable: false, indexType: IndexType.standardIndex)
|
||||
@SerializableField(isNullable: false)
|
||||
bool? get liked;
|
||||
}
|
||||
@@ -33,3 +33,91 @@ abstract class _Scheme extends BaseModel {
|
||||
@DefaultsTo([])
|
||||
List? get gestures;
|
||||
}
|
||||
|
||||
@serializable
|
||||
@Orm(tableName: 'schemes', generateMigrations: false)
|
||||
abstract class _SimpleScheme {
|
||||
@Column()
|
||||
int? id;
|
||||
|
||||
@Column(isNullable: false, indexType: IndexType.unique)
|
||||
@SerializableField(isNullable: false)
|
||||
String? get uuid;
|
||||
|
||||
@Column(isNullable: false)
|
||||
@SerializableField(isNullable: false)
|
||||
String? get name;
|
||||
|
||||
@Column(isNullable: false, indexType: IndexType.standardIndex)
|
||||
@SerializableField(isNullable: true, exclude: true)
|
||||
int? uid;
|
||||
|
||||
@Column(type: ColumnType.text)
|
||||
String? description;
|
||||
|
||||
@Column(isNullable: false, indexType: IndexType.standardIndex)
|
||||
@SerializableField(defaultValue: false, isNullable: false)
|
||||
bool? get shared;
|
||||
|
||||
@SerializableField(isNullable: true)
|
||||
@Column(type: ColumnType.json)
|
||||
Map<String, dynamic>? get metadata;
|
||||
|
||||
@SerializableField(isNullable: true)
|
||||
@Column(expression: 'lr.liked')
|
||||
bool? get liked;
|
||||
}
|
||||
|
||||
@serializable
|
||||
abstract class _SimpleSchemeTransMetaData {
|
||||
@SerializableField(isNullable: false)
|
||||
String? get uuid;
|
||||
|
||||
@SerializableField(isNullable: false)
|
||||
String? get name;
|
||||
|
||||
@SerializableField(isNullable: false)
|
||||
String? description;
|
||||
|
||||
@SerializableField(defaultValue: false, isNullable: false)
|
||||
bool? get shared;
|
||||
|
||||
int? get downloads;
|
||||
|
||||
int? get likes;
|
||||
|
||||
bool? get liked;
|
||||
}
|
||||
|
||||
SimpleSchemeTransMetaData transSimpleSchemeMetaData(SimpleScheme scheme) => SimpleSchemeTransMetaData(
|
||||
description: scheme.description,
|
||||
uuid: scheme.uuid,
|
||||
name: scheme.name,
|
||||
shared: scheme.shared,
|
||||
liked: scheme.liked,
|
||||
likes: scheme.metadata?['likes'] ?? 0,
|
||||
downloads: scheme.metadata?['downloads'] ?? 0,
|
||||
);
|
||||
|
||||
@serializable
|
||||
abstract class _SchemeForDownload {
|
||||
@SerializableField(isNullable: false)
|
||||
String? get uuid;
|
||||
|
||||
@SerializableField(isNullable: false)
|
||||
String? get name;
|
||||
|
||||
@Column(type: ColumnType.text)
|
||||
String? description;
|
||||
|
||||
@SerializableField()
|
||||
@DefaultsTo([])
|
||||
List? get gestures;
|
||||
}
|
||||
|
||||
SchemeForDownload transSchemeForDownload(Scheme scheme) => SchemeForDownload(
|
||||
uuid: scheme.uuid,
|
||||
name: scheme.name,
|
||||
description: scheme.description,
|
||||
gestures: scheme.gestures,
|
||||
);
|
||||
|
||||
@@ -84,7 +84,7 @@ Future configureServer(Angel app) async {
|
||||
chain(
|
||||
[
|
||||
jwtMiddleware(),
|
||||
(req, res) => req.user.blocked == false ? res.noContent() : res.forbidden(),
|
||||
(req, res) => req.user!.blocked == false ? res.noContent() : res.forbidden(),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -48,5 +48,11 @@ extension RedisClient on RequestContext {
|
||||
}
|
||||
|
||||
extension JWTUserInstance on RequestContext {
|
||||
User get user => container!.make<User>();
|
||||
User? get user {
|
||||
try {
|
||||
return container!.make<User>();
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@ import 'package:angel3_framework/angel3_framework.dart';
|
||||
import 'package:dde_gesture_manager_api/models.dart';
|
||||
import '../controllers/controller_extensions.dart';
|
||||
|
||||
RequestHandler jwtMiddleware() {
|
||||
RequestHandler jwtMiddleware({ignoreError = false}) {
|
||||
return (RequestContext req, ResponseContext res, {bool throwError = true}) async {
|
||||
bool _reject(ResponseContext res) {
|
||||
bool _reject(ResponseContext res, [ignoreError = false]) {
|
||||
if (ignoreError) return true;
|
||||
if (throwError) {
|
||||
res.forbidden();
|
||||
}
|
||||
@@ -18,17 +19,22 @@ RequestHandler jwtMiddleware() {
|
||||
if (reqContainer.has<User>() || req.method == 'OPTIONS') {
|
||||
return true;
|
||||
} else if (reqContainer.has<Future<User>>()) {
|
||||
User user = await reqContainer.makeAsync<User>();
|
||||
var authToken = req.container!.make<AuthToken>();
|
||||
if (user.secret(req.app!.configuration['password_salt']) != authToken.payload[UserFields.password]) {
|
||||
return _reject(res);
|
||||
try {
|
||||
User user = await reqContainer.makeAsync<User>();
|
||||
var authToken = req.container!.make<AuthToken>();
|
||||
if (user.secret(req.app!.configuration['password_salt']) != authToken.payload[UserFields.password]) {
|
||||
return _reject(res, ignoreError);
|
||||
}
|
||||
} catch (e) {
|
||||
if (ignoreError) return true;
|
||||
rethrow;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return _reject(res);
|
||||
return _reject(res, ignoreError);
|
||||
}
|
||||
} else {
|
||||
return _reject(res);
|
||||
return _reject(res, ignoreError);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ import 'dart:async';
|
||||
|
||||
import 'package:angel3_framework/angel3_framework.dart';
|
||||
import 'package:dde_gesture_manager_api/apis.dart';
|
||||
import 'package:dde_gesture_manager_api/src/models/download_history.dart';
|
||||
import 'package:dde_gesture_manager_api/src/models/like_record.dart';
|
||||
import 'package:dde_gesture_manager_api/src/models/scheme.dart';
|
||||
import 'package:dde_gesture_manager_api/src/routes/controllers/middlewares.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
@@ -20,16 +22,18 @@ Future configureServer(Angel app) async {
|
||||
var scheme = SchemeSerializer.fromMap(req.bodyAsMap);
|
||||
var schemeQuery = SchemeQuery();
|
||||
schemeQuery.where!.uuid.equals(scheme.uuid!);
|
||||
var one = await schemeQuery.getOne(req.queryExecutor);
|
||||
schemeQuery = SchemeQuery();
|
||||
schemeQuery.values.copyFrom(scheme);
|
||||
schemeQuery.values.uid = int.parse(req.user.id!);
|
||||
if (one.isEmpty) {
|
||||
await schemeQuery.insert(req.queryExecutor);
|
||||
} else {
|
||||
schemeQuery.whereId = int.parse(one.value.id!);
|
||||
await schemeQuery.updateOne(req.queryExecutor);
|
||||
}
|
||||
req.queryExecutor.transaction((tx) async {
|
||||
var one = await schemeQuery.getOne(tx);
|
||||
schemeQuery = SchemeQuery();
|
||||
schemeQuery.values.copyFrom(scheme);
|
||||
schemeQuery.values.uid = req.user!.idAsInt;
|
||||
if (one.isEmpty) {
|
||||
return await schemeQuery.insert(tx);
|
||||
} else {
|
||||
schemeQuery.whereId = one.value.idAsInt;
|
||||
return await schemeQuery.updateOne(tx);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
_log.severe(e);
|
||||
return res.unProcessableEntity();
|
||||
@@ -40,21 +44,145 @@ Future configureServer(Angel app) async {
|
||||
),
|
||||
);
|
||||
|
||||
app.get(
|
||||
Apis.scheme.userUploads,
|
||||
app.post(
|
||||
Apis.scheme.markAsShared.route,
|
||||
chain(
|
||||
[
|
||||
jwtMiddleware(),
|
||||
(req, res) async {
|
||||
var schemeId = req.params['schemeId'];
|
||||
var schemeQuery = SchemeQuery();
|
||||
schemeQuery.where!.uid.equals(int.parse(req.user.id!));
|
||||
schemeQuery.orderBy(SchemeFields.updatedAt, descending: true);
|
||||
return schemeQuery.get(req.queryExecutor).then((value) => value.map((e) => {
|
||||
'name': e.name,
|
||||
'description': e.description,
|
||||
}).toList());
|
||||
schemeQuery.where!.uuid.equals(schemeId);
|
||||
schemeQuery.values.shared = true;
|
||||
await schemeQuery.updateOne(req.queryExecutor);
|
||||
return res.noContent();
|
||||
},
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
app.get(
|
||||
Apis.scheme.user.route,
|
||||
chain(
|
||||
[
|
||||
jwtMiddleware(),
|
||||
(req, res) async {
|
||||
var schemeQuery = SimpleSchemeQuery();
|
||||
var type = req.params['type'];
|
||||
var likeRecordTableName = LikeRecordQuery().tableName;
|
||||
schemeQuery.leftJoin(likeRecordTableName, SchemeFields.id, LikeRecordFields.schemeId, alias: 'lr');
|
||||
|
||||
switch (type) {
|
||||
case 'uploaded':
|
||||
schemeQuery.where!.uid.equals(req.user!.idAsInt);
|
||||
break;
|
||||
case 'downloaded':
|
||||
var downloadHistoryTableName = DownloadHistoryQuery().tableName;
|
||||
schemeQuery.leftJoin(downloadHistoryTableName, SchemeFields.id, DownloadHistoryFields.schemeId,
|
||||
alias: 'dh');
|
||||
schemeQuery.where!.raw('dh.${DownloadHistoryFields.uid} = ${req.user!.idAsInt}');
|
||||
break;
|
||||
case 'liked':
|
||||
schemeQuery.where!.raw('lr.${LikeRecordFields.uid} = ${req.user!.idAsInt}');
|
||||
schemeQuery.where!.raw('lr.${LikeRecordFields.liked} = true');
|
||||
break;
|
||||
default:
|
||||
return res.unProcessableEntity();
|
||||
}
|
||||
schemeQuery.orderBy('${schemeQuery.tableName}.${SchemeFields.updatedAt}', descending: true);
|
||||
return schemeQuery.get(req.queryExecutor).then((value) => value.map(transSimpleSchemeMetaData).toList());
|
||||
},
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
app.get(
|
||||
Apis.scheme.download.route,
|
||||
chain(
|
||||
[
|
||||
jwtMiddleware(ignoreError: true),
|
||||
(req, res) async {
|
||||
var schemeQuery = SchemeQuery();
|
||||
schemeQuery.where?.uuid.equals(req.params['schemeId']);
|
||||
var optionalScheme = await schemeQuery.getOne(req.queryExecutor);
|
||||
if (optionalScheme.isNotEmpty) {
|
||||
var scheme = optionalScheme.value;
|
||||
if (req.user != null) {
|
||||
/// 增加用户下载记录
|
||||
var downloadHistoryQuery = DownloadHistoryQuery();
|
||||
downloadHistoryQuery.where?.uid.equals(req.user!.idAsInt);
|
||||
downloadHistoryQuery.where?.schemeId.equals(scheme.idAsInt);
|
||||
var notExist = (await downloadHistoryQuery.getOne(req.queryExecutor)).isEmpty;
|
||||
if (notExist) {
|
||||
downloadHistoryQuery = DownloadHistoryQuery();
|
||||
downloadHistoryQuery.values.copyFrom(DownloadHistory(uid: req.user!.idAsInt, schemeId: scheme.idAsInt));
|
||||
await downloadHistoryQuery.insert(req.queryExecutor);
|
||||
}
|
||||
}
|
||||
|
||||
/// 增加方案的下载数量
|
||||
schemeQuery = SchemeQuery();
|
||||
schemeQuery.whereId = scheme.idAsInt;
|
||||
Map<String, dynamic> metadata = Map.from(scheme.metadata!);
|
||||
metadata.update('downloads', (value) => ++value, ifAbsent: () => 1);
|
||||
schemeQuery.values.metadata = metadata;
|
||||
schemeQuery.updateOne(req.queryExecutor);
|
||||
|
||||
return res.json(transSchemeForDownload(scheme));
|
||||
}
|
||||
return res.notFound();
|
||||
},
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
app.get(
|
||||
Apis.scheme.like.route,
|
||||
chain(
|
||||
[
|
||||
jwtMiddleware(),
|
||||
(req, res) async {
|
||||
bool isLike = req.params['isLike'] == 'like';
|
||||
bool needUpdate = true;
|
||||
var schemeQuery = SchemeQuery();
|
||||
schemeQuery.where?.uuid.equals(req.params['schemeId']);
|
||||
var optionalScheme = await schemeQuery.getOne(req.queryExecutor);
|
||||
if (optionalScheme.isNotEmpty) {
|
||||
var scheme = optionalScheme.value;
|
||||
if (req.user != null) {
|
||||
/// 增加用户点赞记录
|
||||
var likeRecordQuery = LikeRecordQuery();
|
||||
likeRecordQuery.where?.uid.equals(req.user!.idAsInt);
|
||||
likeRecordQuery.where?.schemeId.equals(scheme.idAsInt);
|
||||
var likeRecordCheck = await likeRecordQuery.getOne(req.queryExecutor);
|
||||
likeRecordQuery = LikeRecordQuery();
|
||||
likeRecordQuery.values
|
||||
.copyFrom(LikeRecord(uid: req.user!.idAsInt, schemeId: scheme.idAsInt, liked: isLike));
|
||||
if (likeRecordCheck.isEmpty) {
|
||||
likeRecordQuery.insert(req.queryExecutor);
|
||||
} else if (likeRecordCheck.value.liked != isLike) {
|
||||
likeRecordQuery.whereId = likeRecordCheck.value.idAsInt;
|
||||
likeRecordQuery.updateOne(req.queryExecutor);
|
||||
} else {
|
||||
needUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (needUpdate) {
|
||||
/// 增加/减少方案的点赞数量
|
||||
schemeQuery = SchemeQuery();
|
||||
schemeQuery.whereId = scheme.idAsInt;
|
||||
Map<String, dynamic> metadata = Map.from(scheme.metadata!);
|
||||
metadata.update('likes', (value) => isLike ? ++value : --value, ifAbsent: () => 1);
|
||||
schemeQuery.values.metadata = metadata;
|
||||
schemeQuery.updateOne(req.queryExecutor);
|
||||
}
|
||||
|
||||
return res.noContent();
|
||||
}
|
||||
return res.notFound();
|
||||
},
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user