wip: me panel.

This commit is contained in:
2022-01-07 18:04:59 +08:00
parent 048c54e080
commit 85a7d36fda
45 changed files with 2776 additions and 67 deletions
+19
View File
@@ -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;
}
+23
View File
@@ -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;
}
+88
View File
@@ -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();
},
],
),
);
}