feat: 接入起卦后端流程并完善积分扣减链路
This commit is contained in:
@@ -0,0 +1,347 @@
|
||||
import 'divination_params.dart';
|
||||
import 'divination_result.dart';
|
||||
|
||||
class PointsBalanceData {
|
||||
const PointsBalanceData({
|
||||
required this.balance,
|
||||
required this.frozenBalance,
|
||||
required this.availableBalance,
|
||||
required this.runCost,
|
||||
required this.canRun,
|
||||
});
|
||||
|
||||
factory PointsBalanceData.fromJson(Map<String, dynamic> json) {
|
||||
return PointsBalanceData(
|
||||
balance: _requiredInt(json, 'balance'),
|
||||
frozenBalance: _requiredInt(json, 'frozenBalance'),
|
||||
availableBalance: _requiredInt(json, 'availableBalance'),
|
||||
runCost: _requiredInt(json, 'runCost'),
|
||||
canRun: _requiredBool(json, 'canRun'),
|
||||
);
|
||||
}
|
||||
|
||||
final int balance;
|
||||
final int frozenBalance;
|
||||
final int availableBalance;
|
||||
final int runCost;
|
||||
final bool canRun;
|
||||
}
|
||||
|
||||
class RunAcceptedData {
|
||||
const RunAcceptedData({
|
||||
required this.taskId,
|
||||
required this.threadId,
|
||||
required this.runId,
|
||||
required this.created,
|
||||
});
|
||||
|
||||
factory RunAcceptedData.fromJson(Map<String, dynamic> json) {
|
||||
return RunAcceptedData(
|
||||
taskId: _requiredString(json, 'taskId'),
|
||||
threadId: _requiredString(json, 'threadId'),
|
||||
runId: _requiredString(json, 'runId'),
|
||||
created: _requiredBool(json, 'created'),
|
||||
);
|
||||
}
|
||||
|
||||
final String taskId;
|
||||
final String threadId;
|
||||
final String runId;
|
||||
final bool created;
|
||||
}
|
||||
|
||||
class DivinationRunAggregate {
|
||||
const DivinationRunAggregate({
|
||||
required this.derived,
|
||||
required this.signLevel,
|
||||
required this.summary,
|
||||
required this.conclusion,
|
||||
required this.focusPoints,
|
||||
required this.advice,
|
||||
required this.keywords,
|
||||
required this.answer,
|
||||
});
|
||||
|
||||
final DerivedDivinationData derived;
|
||||
final String signLevel;
|
||||
final String summary;
|
||||
final List<String> conclusion;
|
||||
final List<String> focusPoints;
|
||||
final List<String> advice;
|
||||
final List<String> keywords;
|
||||
final String answer;
|
||||
|
||||
DivinationResultData toViewData(DivinationParams params) {
|
||||
return DivinationResultData(
|
||||
params: params,
|
||||
binaryCode: derived.binaryCode,
|
||||
changedBinaryCode: derived.changedBinaryCode,
|
||||
guaName: derived.guaName,
|
||||
targetGuaName: derived.targetGuaName,
|
||||
upperName: derived.upperName,
|
||||
lowerName: derived.lowerName,
|
||||
signType: signLevel,
|
||||
keywords: keywords.join('、'),
|
||||
conclusion: _asBullet(conclusion),
|
||||
analysis: summary.isEmpty ? answer : '$summary\n\n$answer',
|
||||
suggestion: _asBullet(advice),
|
||||
ganzhi: GanzhiData(
|
||||
yearGanZhi: derived.ganzhi.yearGanZhi,
|
||||
monthGanZhi: derived.ganzhi.monthGanZhi,
|
||||
dayGanZhi: derived.ganzhi.dayGanZhi,
|
||||
timeGanZhi: derived.ganzhi.timeGanZhi,
|
||||
yearKongWang: derived.ganzhi.yearKongWang,
|
||||
monthKongWang: derived.ganzhi.monthKongWang,
|
||||
dayKongWang: derived.ganzhi.dayKongWang,
|
||||
timeKongWang: derived.ganzhi.timeKongWang,
|
||||
yueJian: derived.ganzhi.yueJian,
|
||||
riChen: derived.ganzhi.riChen,
|
||||
yuePo: derived.ganzhi.yuePo,
|
||||
riChong: derived.ganzhi.riChong,
|
||||
),
|
||||
wuXingStatus: derived.wuXingStatuses,
|
||||
yaoLines: derived.yaoInfoList
|
||||
.map((line) => line.toViewModel())
|
||||
.toList(growable: false),
|
||||
targetYaoLines: derived.targetYaoInfoList
|
||||
.map((line) => line.toViewModel())
|
||||
.toList(growable: false),
|
||||
);
|
||||
}
|
||||
|
||||
String _asBullet(List<String> lines) {
|
||||
if (lines.isEmpty) {
|
||||
return '';
|
||||
}
|
||||
return List<String>.generate(
|
||||
lines.length,
|
||||
(i) => '${i + 1}. ${lines[i]}',
|
||||
growable: false,
|
||||
).join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
class DerivedDivinationData {
|
||||
const DerivedDivinationData({
|
||||
required this.question,
|
||||
required this.questionType,
|
||||
required this.divinationMethod,
|
||||
required this.divinationTime,
|
||||
required this.binaryCode,
|
||||
required this.changedBinaryCode,
|
||||
required this.guaName,
|
||||
required this.upperName,
|
||||
required this.lowerName,
|
||||
required this.targetGuaName,
|
||||
required this.worldPosition,
|
||||
required this.responsePosition,
|
||||
required this.hasChangingYao,
|
||||
required this.ganzhi,
|
||||
required this.wuXingStatuses,
|
||||
required this.yaoInfoList,
|
||||
required this.targetYaoInfoList,
|
||||
});
|
||||
|
||||
factory DerivedDivinationData.fromJson(Map<String, dynamic> json) {
|
||||
final wuXingRaw = _requiredMap(json, 'wuXingStatuses');
|
||||
return DerivedDivinationData(
|
||||
question: _requiredString(json, 'question'),
|
||||
questionType: _requiredString(json, 'questionType'),
|
||||
divinationMethod: _requiredString(json, 'divinationMethod'),
|
||||
divinationTime: _requiredString(json, 'divinationTime'),
|
||||
binaryCode: _requiredString(json, 'binaryCode'),
|
||||
changedBinaryCode: _requiredString(json, 'changedBinaryCode'),
|
||||
guaName: _requiredString(json, 'guaName'),
|
||||
upperName: _requiredString(json, 'upperName'),
|
||||
lowerName: _requiredString(json, 'lowerName'),
|
||||
targetGuaName: _requiredString(json, 'targetGuaName'),
|
||||
worldPosition: _requiredInt(json, 'worldPosition'),
|
||||
responsePosition: _requiredInt(json, 'responsePosition'),
|
||||
hasChangingYao: _requiredBool(json, 'hasChangingYao'),
|
||||
ganzhi: GanzhiBackend.fromJson(_requiredMap(json, 'ganzhi')),
|
||||
wuXingStatuses: wuXingRaw.map(
|
||||
(key, value) => MapEntry(key, value.toString()),
|
||||
),
|
||||
yaoInfoList: _parseYaoList(json['yaoInfoList']),
|
||||
targetYaoInfoList: _parseYaoList(json['targetYaoInfoList']),
|
||||
);
|
||||
}
|
||||
|
||||
final String question;
|
||||
final String questionType;
|
||||
final String divinationMethod;
|
||||
final String divinationTime;
|
||||
final String binaryCode;
|
||||
final String changedBinaryCode;
|
||||
final String guaName;
|
||||
final String upperName;
|
||||
final String lowerName;
|
||||
final String targetGuaName;
|
||||
final int worldPosition;
|
||||
final int responsePosition;
|
||||
final bool hasChangingYao;
|
||||
final GanzhiBackend ganzhi;
|
||||
final Map<String, String> wuXingStatuses;
|
||||
final List<YaoBackendLine> yaoInfoList;
|
||||
final List<YaoBackendLine> targetYaoInfoList;
|
||||
|
||||
static List<YaoBackendLine> _parseYaoList(Object? raw) {
|
||||
final list = raw as List<dynamic>?;
|
||||
if (list == null) {
|
||||
throw const FormatException(
|
||||
'Missing required list: yaoInfoList/targetYaoInfoList',
|
||||
);
|
||||
}
|
||||
return list
|
||||
.map((item) {
|
||||
if (item is! Map<String, dynamic>) {
|
||||
throw const FormatException('Invalid yao line item');
|
||||
}
|
||||
return YaoBackendLine.fromJson(item);
|
||||
})
|
||||
.toList(growable: false);
|
||||
}
|
||||
}
|
||||
|
||||
class GanzhiBackend {
|
||||
const GanzhiBackend({
|
||||
required this.yearGanZhi,
|
||||
required this.monthGanZhi,
|
||||
required this.dayGanZhi,
|
||||
required this.timeGanZhi,
|
||||
required this.yearKongWang,
|
||||
required this.monthKongWang,
|
||||
required this.dayKongWang,
|
||||
required this.timeKongWang,
|
||||
required this.yueJian,
|
||||
required this.riChen,
|
||||
required this.yuePo,
|
||||
required this.riChong,
|
||||
});
|
||||
|
||||
factory GanzhiBackend.fromJson(Map<String, dynamic> json) {
|
||||
return GanzhiBackend(
|
||||
yearGanZhi: _requiredString(json, 'yearGanZhi'),
|
||||
monthGanZhi: _requiredString(json, 'monthGanZhi'),
|
||||
dayGanZhi: _requiredString(json, 'dayGanZhi'),
|
||||
timeGanZhi: _requiredString(json, 'timeGanZhi'),
|
||||
yearKongWang: _requiredString(json, 'yearKongWang'),
|
||||
monthKongWang: _requiredString(json, 'monthKongWang'),
|
||||
dayKongWang: _requiredString(json, 'dayKongWang'),
|
||||
timeKongWang: _requiredString(json, 'timeKongWang'),
|
||||
yueJian: _requiredString(json, 'yueJian'),
|
||||
riChen: _requiredString(json, 'riChen'),
|
||||
yuePo: _requiredString(json, 'yuePo'),
|
||||
riChong: _requiredString(json, 'riChong'),
|
||||
);
|
||||
}
|
||||
|
||||
final String yearGanZhi;
|
||||
final String monthGanZhi;
|
||||
final String dayGanZhi;
|
||||
final String timeGanZhi;
|
||||
final String yearKongWang;
|
||||
final String monthKongWang;
|
||||
final String dayKongWang;
|
||||
final String timeKongWang;
|
||||
final String yueJian;
|
||||
final String riChen;
|
||||
final String yuePo;
|
||||
final String riChong;
|
||||
}
|
||||
|
||||
class YaoBackendLine {
|
||||
const YaoBackendLine({
|
||||
required this.position,
|
||||
required this.spiritName,
|
||||
required this.relationName,
|
||||
required this.tiganName,
|
||||
required this.elementName,
|
||||
required this.isYang,
|
||||
required this.isChanging,
|
||||
required this.specialMark,
|
||||
});
|
||||
|
||||
factory YaoBackendLine.fromJson(Map<String, dynamic> json) {
|
||||
return YaoBackendLine(
|
||||
position: _requiredInt(json, 'position'),
|
||||
spiritName: _requiredString(json, 'spiritName'),
|
||||
relationName: _requiredString(json, 'relationName'),
|
||||
tiganName: _requiredString(json, 'tiganName'),
|
||||
elementName: _requiredString(json, 'elementName'),
|
||||
isYang: _requiredBool(json, 'isYang'),
|
||||
isChanging: _requiredBool(json, 'isChanging'),
|
||||
specialMark: _requiredStringAllowEmpty(json, 'specialMark'),
|
||||
);
|
||||
}
|
||||
|
||||
final int position;
|
||||
final String spiritName;
|
||||
final String relationName;
|
||||
final String tiganName;
|
||||
final String elementName;
|
||||
final bool isYang;
|
||||
final bool isChanging;
|
||||
final String specialMark;
|
||||
|
||||
YaoLineData toViewModel() {
|
||||
final type = switch ((isYang, isChanging)) {
|
||||
(true, false) => YaoType.youngYang,
|
||||
(false, false) => YaoType.youngYin,
|
||||
(true, true) => YaoType.oldYang,
|
||||
(false, true) => YaoType.oldYin,
|
||||
};
|
||||
return YaoLineData(
|
||||
index: position - 1,
|
||||
spirit: spiritName,
|
||||
relation: relationName,
|
||||
branch: tiganName,
|
||||
element: elementName,
|
||||
type: type,
|
||||
mark: specialMark,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _requiredStringAllowEmpty(Map<String, dynamic> json, String key) {
|
||||
if (!json.containsKey(key)) {
|
||||
throw FormatException('Invalid or missing field: $key');
|
||||
}
|
||||
final value = json[key];
|
||||
if (value is! String) {
|
||||
throw FormatException('Invalid or missing field: $key');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
String _requiredString(Map<String, dynamic> json, String key) {
|
||||
final value = json[key];
|
||||
if (value is! String || value.isEmpty) {
|
||||
throw FormatException('Invalid or missing field: $key');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
int _requiredInt(Map<String, dynamic> json, String key) {
|
||||
final value = json[key];
|
||||
if (value is! num) {
|
||||
throw FormatException('Invalid or missing field: $key');
|
||||
}
|
||||
return value.toInt();
|
||||
}
|
||||
|
||||
bool _requiredBool(Map<String, dynamic> json, String key) {
|
||||
final value = json[key];
|
||||
if (value is! bool) {
|
||||
throw FormatException('Invalid or missing field: $key');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
Map<String, dynamic> _requiredMap(Map<String, dynamic> json, String key) {
|
||||
final value = json[key];
|
||||
if (value is! Map<String, dynamic>) {
|
||||
throw FormatException('Invalid or missing field: $key');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
Reference in New Issue
Block a user