载入中...
搜索中...
未找到
DialoguePayment.cpp
浏览该文件的文档.
2
4
5#include <algorithm>
6#include <array>
7#include <string_view>
8#include <utility>
9
10namespace eve::dialogue {
11namespace {
12
13eve::Result<std::int64_t> positiveInteger(const eve::Value& value, std::string_view path) {
14 if (!value.isInt64())
16 "dialogue payment amount must be an Int64",
17 std::string(path), {}, "dialogue.payment"));
18 const std::int64_t amount = value.asInt();
19 if (amount <= 0)
21 "dialogue payment amount must be positive",
22 std::string(path), {}, "dialogue.payment"));
24}
25
26struct AccountCost {
29};
30
31eve::Result<std::vector<AccountCost>> buildCosts(const DialogueAccountBindings& bindings, const PaymentSpec& payment) {
32 auto valid = payment.validate();
33 if (!valid) return eve::Result<std::vector<AccountCost>>::failure(valid.status());
34
35 struct Raw {
37 const std::string* resource;
38 std::optional<std::int64_t> amount;
39 const char* path;
40 };
41 const std::array<Raw, 2> raw{{
42 {bindings.money, &bindings.moneyResource, payment.money, "money"},
43 {bindings.reputation, &bindings.reputationResource, payment.reputation, "reputation"},
44 }};
45
46 std::vector<AccountCost> result;
47 for (const auto& item : raw) {
48 if (!item.amount) continue;
49 if (item.account == nullptr)
52 std::string("dialogue ") + item.path + " payment has no account adapter",
53 item.path, {}, "dialogue.payment"));
54 auto costItem = eve::resource::ResourceCost::create(*item.resource, *item.amount);
55 if (!costItem) return eve::Result<std::vector<AccountCost>>::failure(costItem.status());
56
57 auto found = std::find_if(result.begin(), result.end(),
58 [&](const AccountCost& candidate) { return candidate.account == item.account; });
59 if (found == result.end()) {
60 std::vector<eve::resource::ResourceCost> items;
61 items.push_back(std::move(costItem).takeValue());
62 auto cost = eve::resource::CostSpec::create(std::move(items));
63 if (!cost) return eve::Result<std::vector<AccountCost>>::failure(cost.status());
64 result.push_back({item.account, std::move(cost).takeValue()});
65 } else {
66 std::vector<eve::resource::ResourceCost> items = found->cost.items();
67 items.push_back(std::move(costItem).takeValue());
68 auto cost = eve::resource::CostSpec::create(std::move(items));
69 if (!cost) return eve::Result<std::vector<AccountCost>>::failure(cost.status());
70 found->cost = std::move(cost).takeValue();
71 }
72 }
73 return eve::Result<std::vector<AccountCost>>::success(std::move(result));
74}
75
76
77} // namespace
78
80 if (money && *money <= 0)
82 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "dialogue money payment must be positive", "money", {}, "dialogue.payment"));
83 if (reputation && *reputation <= 0)
85 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "dialogue reputation payment must be positive", "reputation", {}, "dialogue.payment"));
87}
88
90 if (!value.isObject())
92 "dialogue payment must be an object", "payment",
93 {}, "dialogue.payment"));
94 PaymentSpec result;
95 for (const auto& key : value.keys()) {
96 const eve::Value* member = value.find(key);
97 if (key == "money") {
98 auto amount = positiveInteger(*member, "money");
99 if (!amount) return eve::Result<PaymentSpec>::failure(amount.status());
100 result.money = std::move(amount).takeValue();
101 } else if (key == "reputation") {
102 auto amount = positiveInteger(*member, "reputation");
103 if (!amount) return eve::Result<PaymentSpec>::failure(amount.status());
104 result.reputation = std::move(amount).takeValue();
105 } else {
107 "unknown dialogue payment field",
108 "payment." + key, {}, "dialogue.payment"));
109 }
110 }
111 return eve::Result<PaymentSpec>::success(std::move(result));
112}
113
116 if (money) object.emplace("money", eve::Value(*money));
117 if (reputation) object.emplace("reputation", eve::Value(*reputation));
118 return eve::Value(std::move(object));
119}
120
122 std::span<const eve::StateMutation> mutations)
123 : provider_(provider), mutations_(mutations.begin(), mutations.end()) {}
124
126 if (context.transactionId().empty())
128 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "dialogue state transaction requires a transaction id", "transactionId", {}, "dialogue.payment"));
129 if (phase_ != Phase::Idle) return eve::Result<void>::failure(
130 eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "dialogue state participant is not idle", "transaction.lifecycle", {}, "dialogue.payment"));
131 phase_ = Phase::Prepared;
133}
134
136 if (phase_ != Phase::Prepared) return eve::Result<void>::failure(
137 eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "dialogue state participant has no prepared stage", "transaction.lifecycle", {}, "dialogue.payment"));
138 auto applied = provider_.apply(
139 mutations_, eve::MutationContext{context.transactionId(), context.correlationId(), context.causationId()});
140 if (!applied) return eve::Result<void>::failure(applied.status());
141 (void)std::move(applied).takeValue();
142 phase_ = Phase::Committed;
144}
145
147 if (phase_ != Phase::Prepared)
149 eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "dialogue state participant has no prepared stage to roll back", "transaction.lifecycle", {}, "dialogue.payment"));
150 phase_ = Phase::RolledBack;
152}
153
156 eve::Diagnostic::error(eve::DiagnosticCode::Unsupported, "dialogue state mutation has no generic inverse; it must be last", "transaction.compensation", {}, "dialogue.payment"));
157}
158
160
162
164 std::string transactionId, const PaymentSpec& payment,
165 std::span<eve::transaction::ITransactionParticipant*> effects) const {
166 if (transactionId.empty())
168 eve::DiagnosticCode::InvalidArgument, "dialogue payment transaction id must not be empty", "transactionId",
169 {}, "dialogue.payment"));
170
171 for (std::size_t index = 0; index < effects.size(); ++index) {
172 if (effects[index] == nullptr)
174 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "dialogue payment effect must not be null",
175 "effects[" + std::to_string(index) + "]", {}, "dialogue.payment"));
176 for (std::size_t prior = 0; prior < index; ++prior) {
177 if (effects[prior] == effects[index])
179 eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "dialogue payment effects must be unique",
180 "effects[" + std::to_string(index) + "]", {}, "dialogue.payment"));
181 }
182 }
183
184 auto costs = buildCosts(bindings_, payment);
185 if (!costs) return eve::Result<eve::transaction::TransactionReceipt>::failure(costs.status());
186 auto accountCosts = std::move(costs).takeValue();
187
188 std::vector<std::unique_ptr<eve::transaction::ResourceDebitParticipant>> debitParticipants;
189 debitParticipants.reserve(accountCosts.size());
190 std::vector<eve::transaction::ITransactionParticipant*> participants;
191 participants.reserve(accountCosts.size() + effects.size());
192 for (std::size_t index = 0; index < accountCosts.size(); ++index) {
193 debitParticipants.push_back(std::make_unique<eve::transaction::ResourceDebitParticipant>(
194 *accountCosts[index].account, std::move(accountCosts[index].cost),
195 "dialogue.resource-debit[" + std::to_string(index) + "]"));
196 participants.push_back(debitParticipants.back().get());
197 }
198 for (auto* effect : effects) participants.push_back(effect);
199 if (participants.empty())
201 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "dialogue payment has no account or effect",
202 "payment", {}, "dialogue.payment"));
203
205 return coordinator.execute(eve::transaction::TransactionContext(std::move(transactionId)), participants);
206}
207
209 std::string transactionId, const PaymentSpec& payment, eve::IStateMutation& provider,
210 std::span<const eve::StateMutation> mutations) const {
211 DialogueStateMutationParticipant state(provider, mutations);
212 std::array<eve::transaction::ITransactionParticipant*, 1> effects{&state};
213 return execute(std::move(transactionId), payment, effects);
214}
215
216} // namespace eve::dialogue
double value
eve::resource::CostSpec cost
eve::resource::IResourceAccount * account
Dialogue payment specifications and transaction composition.
std::uint32_t key
bool valid
std::vector< std::shared_ptr< DeviceBytes > > bindings
Definition OnnxGpgpu.cpp:38
std::unique_ptr< ParticleEffect > effect
std::string path
Definition PlayHost.cpp:110
float begin
Transaction participant for an atomic resource debit.
bool found
std::string resource
Json object
float(ui::Theme::* member)[4]
uint32_t index
const VegetationPresetContext & context
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
std::map< std::string, Value > Object
Definition Value.h:34
Value * find(const std::string &key) noexcept
Return an object member, or nullptr when absent.
Definition Value.cpp:124
DialoguePaymentAdapter(DialogueAccountBindings bindings={})
Construct an adapter with borrowed account bindings.
void setBindings(DialogueAccountBindings bindings)
Replace borrowed account bindings; no balance is copied.
const DialogueAccountBindings & bindings() const noexcept
Return the current borrowed bindings for diagnostics.
eve::Result< eve::transaction::TransactionReceipt > executeStateMutation(std::string transactionId, const PaymentSpec &payment, eve::IStateMutation &provider, std::span< const eve::StateMutation > mutations) const
Execute a payment together with one StatePatch-compatible mutation set.
eve::Result< eve::transaction::TransactionReceipt > execute(std::string transactionId, const PaymentSpec &payment, std::span< eve::transaction::ITransactionParticipant * > effects) const
Execute a payment and zero or more staged domain effects atomically.
Transaction participant that delegates an atomic state mutation.
eve::Result< void > compensate(const eve::transaction::TransactionContext &context) override
Undo an already committed external effect by compensation.
eve::Result< void > commit(const eve::transaction::TransactionContext &context) override
Publish this participant's staged changes.
eve::Result< void > rollback(const eve::transaction::TransactionContext &context) override
Discard a prepared but not committed stage.
eve::Result< void > prepare(const eve::transaction::TransactionContext &context) override
Validate and stage local changes for the transaction.
DialogueStateMutationParticipant(eve::IStateMutation &provider, std::span< const eve::StateMutation > mutations)
Bind a copied mutation set to one borrowed mutation provider.
Immutable canonical multi-resource cost.
static eve::Result< CostSpec > create(std::vector< ResourceCost > items)
Validate and canonicalize resource items.
Provider-neutral resource account contract.
All-or-nothing mutation boundary owned by the consumer.
virtual eve::Result< MutationReceipt > apply(std::span< const StateMutation > mutations, const MutationContext &context)=0
Apply a complete mutation set atomically.
Coordinates participant lifecycles with deterministic ordering.
Result< TransactionReceipt > execute(const TransactionContext &context, std::span< ITransactionParticipant * > participants) const
Atomically execute borrowed participants for one context.
Immutable context shared by every participant of one transaction.
Definition Transaction.h:30
Borrowed account bindings used by one DialogueFlow integration.
Canonical money/reputation cost attached to a dialogue choice or command.
std::optional< std::int64_t > money
Optional money amount, in the configured money resource.
eve::Result< void > validate() const
Validate that every present amount is a positive integer.
eve::Value toValue() const
Project this payment into a deterministic canonical Value object.
static eve::Result< PaymentSpec > fromValue(const eve::Value &value)
Parse { "money": n, "reputation": n } from canonical Value.
std::optional< std::int64_t > reputation
Optional reputation amount, in the configured reputation resource.
static eve::Result< ResourceCost > create(std::string_view resourceId, std::int64_t amountValue)
Construct and validate one cost item.
Correlation metadata shared by a mutation transaction.
Definition StateAccess.h:57