Merge remote-tracking branch 'origin/master'

chore/issuance-comments-zh
赵佳正 3 weeks ago
commit 32c5cae953

@ -0,0 +1,45 @@
package com.yau.digitalrmb.shared.wallet;
import com.yau.digitalrmb.identity.domain.UserRole;
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject;
import com.yau.digitalrmb.security.context.AuthContextHolder;
import com.yau.digitalrmb.security.context.JwtUser;
import com.yau.digitalrmb.shared.api.ErrorCode;
import com.yau.digitalrmb.shared.exception.BusinessException;
import com.yau.digitalrmb.shared.api.ApiResponse;
import com.yau.digitalrmb.shared.web.TraceIdFilter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.MDC;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/api/v1/wallet-runtime")
@Tag(name = "钱包运行数据-彻底重开", description = "清空兑换/支付历史并重建模块三投影(教师/管理员专用)")
public class WalletRuntimeResetController {
@Resource
private WalletRuntimeResetService resetService;
@PostMapping("/reset")
@Operation(summary = "彻底重开兑换与支付实验",
description = "删除当前用户的全部兑换/支付订单与流水(含已确权完成的),把涉及币串回退到商业银行库," +
"并清空模块三投影出的共享钱包/银行账户/证书/合约/绑定与实验attempt," +
"随后立即按最新模块三结果重新投影,模块四/五可直接重新开始。仅教师和管理员可调用。")
public ApiResponse<WalletRuntimeResetResult> reset() {
JwtUser jwtUser = AuthContextHolder.get();
Integer roleId = jwtUser.getRoleId();
if (roleId == null || (roleId != UserRole.ADMIN.getId() && roleId != UserRole.TEACHER.getId())) {
throw new BusinessException(ErrorCode.FORBIDDEN, "仅教师或管理员可以彻底重开兑换与支付实验");
}
return ApiResponse.success(resetService.reset(InstitutionKeySubject.from(jwtUser)), traceId());
}
private String traceId() {
return MDC.get(TraceIdFilter.MDC_KEY);
}
}

@ -0,0 +1,31 @@
package com.yau.digitalrmb.shared.wallet;
import io.swagger.v3.oas.annotations.media.Schema;
public class WalletRuntimeResetResult {
@Schema(description = "删除的兑换订单数")
private final int deletedExchangeOrderCount;
@Schema(description = "删除的支付订单数")
private final int deletedPaymentOrderCount;
@Schema(description = "回退到商业银行库的币串数")
private final int restoredCoinCount;
@Schema(description = "删除的共享钱包数")
private final int deletedWalletCount;
@Schema(description = "删除的实验attempt数")
private final int deletedAttemptCount;
public WalletRuntimeResetResult(int deletedExchangeOrderCount, int deletedPaymentOrderCount,
int restoredCoinCount, int deletedWalletCount, int deletedAttemptCount) {
this.deletedExchangeOrderCount = deletedExchangeOrderCount;
this.deletedPaymentOrderCount = deletedPaymentOrderCount;
this.restoredCoinCount = restoredCoinCount;
this.deletedWalletCount = deletedWalletCount;
this.deletedAttemptCount = deletedAttemptCount;
}
public int getDeletedExchangeOrderCount() { return deletedExchangeOrderCount; }
public int getDeletedPaymentOrderCount() { return deletedPaymentOrderCount; }
public int getRestoredCoinCount() { return restoredCoinCount; }
public int getDeletedWalletCount() { return deletedWalletCount; }
public int getDeletedAttemptCount() { return deletedAttemptCount; }
}

@ -0,0 +1,187 @@
package com.yau.digitalrmb.shared.wallet;
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@Service
public class WalletRuntimeResetService {
private final JdbcTemplate jdbc;
private final WalletPrerequisiteProjectionService projectionService;
public WalletRuntimeResetService(JdbcTemplate jdbc, WalletPrerequisiteProjectionService projectionService) {
this.jdbc = jdbc;
this.projectionService = projectionService;
}
@Transactional
public WalletRuntimeResetResult reset(InstitutionKeySubject subject) {
String userId = subject.getUserId();
List<String> exchangeIds = jdbc.queryForList(
"SELECT id FROM currency_exchange_order WHERE user_id=?", String.class, userId);
List<String> paymentIds = jdbc.queryForList(
"SELECT id FROM payment_order WHERE payer_user_id=? OR payee_user_id=?",
String.class, userId, userId);
List<String> ownWalletIds = jdbc.queryForList(
"SELECT wallet_id FROM digital_wallet WHERE user_id=?", String.class, userId);
int restoredCoins = restoreCoinsToBankVault(exchangeIds, paymentIds, ownWalletIds);
reverseCounterpartyBalances(paymentIds, userId);
deleteExchangeRows(exchangeIds, userId);
deletePaymentRows(paymentIds);
int deletedWallets = deleteSharedRuntime(userId, ownWalletIds);
int deletedAttempts = deleteAttempts(userId);
deleteTaskProgress(userId);
// 实训4/5 的上下文接口只读取共享投影,不会自行重建;删除后必须立即按最新的实训3结果重新投影,
// 否则清空后页面会一直停在「请先完成实训3」。实训3本身已被重新实训(无完成结果)时跳过重建。
if (hasCompletedModuleThree(subject)) {
projectionService.ensureForSubject(subject);
}
return new WalletRuntimeResetResult(exchangeIds.size(), paymentIds.size(), restoredCoins,
deletedWallets, deletedAttempts);
}
private boolean hasCompletedModuleThree(InstitutionKeySubject subject) {
Integer count = jdbc.queryForObject("SELECT COUNT(*) FROM central_wallet_activation " +
"WHERE user_id=? AND school_id=? AND class_id=? AND wallet_activated=TRUE AND final_sent=TRUE " +
"AND cb_final_signature IS NOT NULL AND status='FINAL_SENT' AND deleted=FALSE",
Integer.class, subject.getUserId(), subject.getSchoolId(), subject.getClassId());
return count != null && count > 0;
}
private int restoreCoinsToBankVault(List<String> exchangeIds, List<String> paymentIds,
List<String> ownWalletIds) {
Set<String> currencyIds = new LinkedHashSet<>();
if (!exchangeIds.isEmpty()) {
currencyIds.addAll(queryCurrencyIdsByBusiness("EXCHANGE", exchangeIds));
}
if (!paymentIds.isEmpty()) {
currencyIds.addAll(queryCurrencyIdsByBusiness("PAYMENT", paymentIds));
currencyIds.addAll(jdbc.queryForList("SELECT DISTINCT currency_id FROM payment_coin_reservation WHERE "
+ in(paymentIds.size()), String.class, paymentIds.toArray()));
}
if (!ownWalletIds.isEmpty()) {
currencyIds.addAll(jdbc.queryForList("SELECT currency_id FROM central_bank_currency_ownership "
+ "WHERE owner_type='WALLET' AND owner_id IN (" + in(ownWalletIds.size()) + ")",
String.class, ownWalletIds.toArray()));
}
for (String currencyId : currencyIds) {
List<String> bankCodes = jdbc.queryForList(
"SELECT bank_code FROM commercial_bank_currency WHERE currency_id=?",
String.class, currencyId);
if (bankCodes.isEmpty()) continue;
jdbc.update("UPDATE central_bank_currency_ownership SET owner_type='BANK',owner_id=?," +
"status='AVAILABLE',lock_business_type=NULL,lock_business_id=NULL," +
"last_transaction_id=NULL,updated_at=CURRENT_TIMESTAMP WHERE currency_id=?",
bankCodes.get(0), currencyId);
}
return currencyIds.size();
}
private List<String> queryCurrencyIdsByBusiness(String businessType, List<String> businessIds) {
List<Object> args = new ArrayList<>();
args.add(businessType);
args.addAll(businessIds);
return jdbc.queryForList("SELECT currency_id FROM central_bank_currency_ownership " +
"WHERE lock_business_type=? AND lock_business_id IN (" + in(businessIds.size()) + ")",
String.class, args.toArray());
}
private void reverseCounterpartyBalances(List<String> paymentIds, String userId) {
for (String paymentId : paymentIds) {
List<CounterpartyFlow> flows = jdbc.query("SELECT payer_user_id,payer_wallet_id,payee_user_id," +
"payee_wallet_id,amount FROM payment_order WHERE id=?",
(rs, row) -> new CounterpartyFlow(rs.getString("payer_user_id"), rs.getString("payer_wallet_id"),
rs.getString("payee_user_id"), rs.getString("payee_wallet_id"),
rs.getBigDecimal("amount")), paymentId);
if (flows.isEmpty()) continue;
CounterpartyFlow flow = flows.get(0);
if (!userId.equals(flow.payerUserId)) {
jdbc.update("UPDATE digital_wallet SET balance=balance+?,updated_at=CURRENT_TIMESTAMP " +
"WHERE wallet_id=?", flow.amount, flow.payerWalletId);
}
if (!userId.equals(flow.payeeUserId)) {
jdbc.update("UPDATE digital_wallet SET balance=balance-?,updated_at=CURRENT_TIMESTAMP " +
"WHERE wallet_id=?", flow.amount, flow.payeeWalletId);
}
}
}
private void deleteExchangeRows(List<String> exchangeIds, String userId) {
deleteIn("exchange_debit_record", "exchange_id", exchangeIds);
deleteIn("wallet_balance_ledger", "exchange_id", exchangeIds);
deleteIn("exchange_coin_reservation", "exchange_id", exchangeIds);
deleteIn("currency_ownership_transfer", "exchange_id", exchangeIds);
deleteIn("exchange_step_log", "exchange_id", exchangeIds);
jdbc.update("DELETE FROM currency_exchange_order WHERE user_id=?", userId);
}
private void deletePaymentRows(List<String> paymentIds) {
deleteIn("payment_step_log", "payment_id", paymentIds);
deleteIn("payment_notification", "payment_id", paymentIds);
deleteIn("payment_wallet_ledger", "payment_id", paymentIds);
deleteIn("payment_clearing_record", "payment_id", paymentIds);
deleteIn("payment_ownership_transfer", "payment_id", paymentIds);
deleteIn("payment_coin_reservation", "payment_id", paymentIds);
deleteIn("payment_order", "id", paymentIds);
}
private int deleteSharedRuntime(String userId, List<String> ownWalletIds) {
deleteIn("wallet_bank_binding", "wallet_id", ownWalletIds);
deleteIn("wallet_contract", "wallet_id", ownWalletIds);
deleteIn("wallet_certificate", "wallet_id", ownWalletIds);
jdbc.update("DELETE FROM digital_wallet WHERE user_id=?", userId);
jdbc.update("DELETE FROM simulated_bank_account WHERE user_id=?", userId);
return ownWalletIds.size();
}
private int deleteAttempts(String userId) {
jdbc.update("DELETE FROM training_experiment_action WHERE attempt_id IN " +
"(SELECT id FROM training_experiment_attempt WHERE user_id=? AND module_code IN ('EXCHANGE','PAYMENT'))",
userId);
return jdbc.update("DELETE FROM training_experiment_attempt WHERE user_id=? " +
"AND module_code IN ('EXCHANGE','PAYMENT')", userId);
}
private void deleteTaskProgress(String userId) {
jdbc.update("DELETE FROM training_task_step_progress WHERE user_id=? " +
"AND module_code IN ('CURRENCY_EXCHANGE','CURRENCY_PAYMENT')", userId);
}
private int deleteIn(String table, String column, List<String> values) {
if (values.isEmpty()) return 0;
return jdbc.update("DELETE FROM " + table + " WHERE " + column + " IN (" + in(values.size()) + ")",
values.toArray());
}
private String in(int count) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < count; i++) {
if (i > 0) builder.append(',');
builder.append('?');
}
return builder.toString();
}
private static final class CounterpartyFlow {
private final String payerUserId;
private final String payerWalletId;
private final String payeeUserId;
private final String payeeWalletId;
private final BigDecimal amount;
private CounterpartyFlow(String payerUserId, String payerWalletId, String payeeUserId,
String payeeWalletId, BigDecimal amount) {
this.payerUserId = payerUserId; this.payerWalletId = payerWalletId;
this.payeeUserId = payeeUserId; this.payeeWalletId = payeeWalletId;
this.amount = amount;
}
}
}

@ -0,0 +1,192 @@
package com.yau.digitalrmb.shared.wallet;
import com.jayway.jsonpath.JsonPath;
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeyService;
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject;
import com.yau.digitalrmb.institutionidentity.domain.InstitutionIdentityCryptography;
import com.yau.digitalrmb.institutionidentity.domain.InstitutionSm2KeyPair;
import com.yau.digitalrmb.testsupport.WalletOpeningTestData;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import java.math.BigDecimal;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(properties = "file.path=target/test-files")
@AutoConfigureMockMvc
@ActiveProfiles("test")
class WalletRuntimeResetTest {
private static final String USER_ID = "00000000-0000-0000-0000-000000000995";
private static final long SCHOOL_ID = 1995L;
private static final long CLASS_ID = 2995L;
private static final String BANK_CODE = "BKCHCNBJ00995";
private static final String WALLET_ID = "WALLET_RUNTIME_RESET";
private static final long WALLET_FACT_ID = 995100L;
@Autowired private MockMvc mockMvc;
@Autowired private JdbcTemplate jdbc;
@Autowired private InstitutionIdentityCryptography cryptography;
@Autowired private InstitutionKeyService keyService;
@Autowired private WalletPrerequisiteProjectionService walletProjection;
@BeforeEach
void seedModuleThreeAndIssuedCoins() {
clean();
jdbc.update("INSERT INTO sys_user (user_id,student_id,password,user_name,class_id,class_name,school_id,school_name,"
+ "role_id,is_deleted,zy_user_id) VALUES (?,?,?,?,?,?,?,?,4,0,?)",
USER_ID, "runtime-reset-test", "unused", "runtime-reset-test", "2995", "重置测试班", "1995", "延安大学", "995");
jdbc.update("INSERT INTO institution_identifier_application (id,bank_code,user_id,school_id,class_id," +
"request_timestamp,original_text,status,institution_identifier,training_round,scoring_criteria," +
"created_at,updated_at,created_by,updated_by,deleted) VALUES " +
"(995001,?,?,1995,2995,'20260918090000','TEST','ISSUED','ORG_RUNTIME_RESET',1,0," +
"CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,'test','test',FALSE)", BANK_CODE, USER_ID);
InstitutionKeySubject subject = new InstitutionKeySubject(USER_ID, SCHOOL_ID, CLASS_ID);
keyService.commercialBankKey(subject, "test");
keyService.centralBankKey(subject, "test");
String issuanceSignature = keyService.signCentralBank(subject, "RUNTIME_RESET_ORIGINAL");
InstitutionSm2KeyPair walletKey = cryptography.generateSm2KeyPair();
WalletOpeningTestData.insertCompletedWallet(jdbc, WALLET_FACT_ID, USER_ID, SCHOOL_ID, CLASS_ID,
WALLET_ID, walletKey.getPrivateKey(), walletKey.getPublicKey(), "中国银行北京分行",
"6216610100001234567", new BigDecimal("50000.00"));
walletProjection.ensureForSubject(subject);
for (int i = 1; i <= 2; i++) {
jdbc.update("INSERT INTO commercial_bank_currency (currency_id,source_currency_id,source_batch_id,"
+ "issuance_request_id,bank_code,organization_id,denomination,currency,complete_currency,"
+ "issuance_ownership_original_text,issuance_ownership_signature,status,"
+ "received_at,received_by_user_id,received_by) VALUES (?,?,?,?,?,?,100,'DC',?,"
+ "'RUNTIME_RESET_ORIGINAL',?,'AVAILABLE',CURRENT_TIMESTAMP,?,'test')",
"DC_RUNTIME_RESET_" + i, 995000 + i, 995000, "RUNTIME_RESET_ISSUANCE", BANK_CODE,
"ORG_RUNTIME_RESET", "DC_RUNTIME_COMPLETE_" + i, issuanceSignature, USER_ID);
jdbc.update("INSERT INTO central_bank_currency_ownership (currency_id,owner_type,owner_id,status," +
"last_transaction_id,updated_at) VALUES (?,'BANK',?,'AVAILABLE',NULL,CURRENT_TIMESTAMP)",
"DC_RUNTIME_RESET_" + i, BANK_CODE);
}
}
@AfterEach
void clean() {
jdbc.update("DELETE FROM exchange_step_log WHERE operator_user_id=?", USER_ID);
jdbc.update("DELETE FROM wallet_balance_ledger WHERE wallet_id=?", WALLET_ID);
jdbc.update("DELETE FROM exchange_coin_reservation WHERE reserved_by='test' AND currency_id LIKE 'DC_RUNTIME_RESET_%'");
jdbc.update("DELETE FROM currency_ownership_transfer WHERE to_wallet_id=?", WALLET_ID);
jdbc.update("DELETE FROM exchange_debit_record WHERE bank_account_id IN " +
"(SELECT account_id FROM simulated_bank_account WHERE user_id=?)", USER_ID);
jdbc.update("DELETE FROM currency_exchange_order WHERE user_id=?", USER_ID);
jdbc.update("DELETE FROM training_experiment_action WHERE attempt_id IN " +
"(SELECT id FROM training_experiment_attempt WHERE user_id=?)", USER_ID);
jdbc.update("DELETE FROM training_experiment_attempt WHERE user_id=?", USER_ID);
jdbc.update("DELETE FROM wallet_bank_binding WHERE wallet_id=?", WALLET_ID);
jdbc.update("DELETE FROM wallet_contract WHERE wallet_id=?", WALLET_ID);
jdbc.update("DELETE FROM wallet_certificate WHERE wallet_id=?", WALLET_ID);
jdbc.update("DELETE FROM simulated_bank_account WHERE user_id=?", USER_ID);
jdbc.update("DELETE FROM digital_wallet WHERE user_id=?", USER_ID);
jdbc.update("DELETE FROM central_bank_currency_ownership WHERE currency_id LIKE 'DC_RUNTIME_RESET_%'");
jdbc.update("DELETE FROM commercial_bank_currency WHERE issuance_request_id='RUNTIME_RESET_ISSUANCE'");
WalletOpeningTestData.deleteWalletFacts(jdbc, USER_ID);
jdbc.update("DELETE FROM institution_sm2_key_audit WHERE user_id=?", USER_ID);
jdbc.update("DELETE FROM institution_sm2_key WHERE user_id=?", USER_ID);
jdbc.update("DELETE FROM institution_identifier_application WHERE user_id=?", USER_ID);
jdbc.update("DELETE FROM sys_user WHERE user_id=?", USER_ID);
}
@Test
void teacherResetRollsBackCompletedExchangeAndReprojectsFreshModuleThree() throws Exception {
String context = mockMvc.perform(get("/api/v1/exchanges/context").with(role(4)))
.andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
String walletId = JsonPath.read(context, "$.data.walletId");
String accountId = JsonPath.read(context, "$.data.bankAccountId");
String created = mockMvc.perform(post("/api/v1/exchanges").with(role(4))
.contentType(MediaType.APPLICATION_JSON)
.content("{\"walletId\":\"" + walletId + "\",\"bankAccountId\":\"" + accountId
+ "\",\"amount\":200.00}"))
.andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
String id = JsonPath.read(created, "$.data.id");
mockMvc.perform(post("/api/v1/exchanges/{id}/sign", id).with(role(4))).andExpect(status().isOk());
mockMvc.perform(post("/api/v1/exchanges/{id}/bank-process", id).with(role(4))).andExpect(status().isOk());
mockMvc.perform(post("/api/v1/exchanges/{id}/reserve-coins", id).with(role(4))).andExpect(status().isOk());
mockMvc.perform(post("/api/v1/exchanges/{id}/confirm-ownership", id).with(role(4)))
.andExpect(status().isOk()).andExpect(jsonPath("$.data.status").value("SUCCESS"));
assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM central_bank_currency_ownership " +
"WHERE owner_type='WALLET' AND owner_id=?", Integer.class, walletId)).isEqualTo(2);
// 模块三重新实训:换卡换余额。共享账户已有兑换历史,属于不能被自动跟随的在用账户。
retrainModuleThreeWith("6216610100009876543", new BigDecimal("8800.00"));
mockMvc.perform(get("/api/v1/exchanges/context").with(role(4)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.bankCardNumber").value("6216610100001234567"));
mockMvc.perform(post("/api/v1/wallet-runtime/reset").with(role(3)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.deletedExchangeOrderCount").value(1))
.andExpect(jsonPath("$.data.restoredCoinCount").value(2))
.andExpect(jsonPath("$.data.deletedWalletCount").value(1));
assertThat(userCount("currency_exchange_order")).isZero();
assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM exchange_step_log WHERE operator_user_id=?",
Integer.class, USER_ID)).isZero();
assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM wallet_balance_ledger WHERE wallet_id=?",
Integer.class, walletId)).isZero();
assertThat(userCount("training_experiment_attempt")).isZero();
assertThat(jdbc.queryForObject("SELECT owner_type || '|' || owner_id || '|' || status " +
"FROM central_bank_currency_ownership WHERE currency_id='DC_RUNTIME_RESET_1'", String.class))
.isEqualTo("BANK|" + BANK_CODE + "|AVAILABLE");
// 重置接口当场按最新的模块三结果重建了投影:卡号与余额取自新的模块三快照,
// 证书/合约/绑定是否随之齐备由最后的 context 断言验证(其查询要求三者 INNER JOIN 命中)
assertThat(userCount("digital_wallet")).isEqualTo(1);
assertThat(userCount("simulated_bank_account")).isEqualTo(1);
assertThat(jdbc.queryForObject("SELECT card_number FROM simulated_bank_account WHERE user_id=?",
String.class, USER_ID)).isEqualTo("6216610100009876543");
assertThat(jdbc.queryForObject("SELECT balance FROM simulated_bank_account WHERE user_id=?",
BigDecimal.class, USER_ID)).isEqualByComparingTo("8800.00");
assertThat(jdbc.queryForObject("SELECT balance FROM digital_wallet WHERE user_id=?",
BigDecimal.class, USER_ID)).isEqualByComparingTo("0.00");
mockMvc.perform(get("/api/v1/exchanges/context").with(role(4)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.bankCardNumber").value("6216610100009876543"))
.andExpect(jsonPath("$.data.bankAccountBalance").value(8800.00));
}
private void retrainModuleThreeWith(String cardNumber, BigDecimal accountBalance) {
jdbc.update("UPDATE wallet_application SET bank_card_number=?,account_balance=?," +
"updated_at=CURRENT_TIMESTAMP WHERE id=?", cardNumber, accountBalance, WALLET_FACT_ID);
}
@Test
void studentRoleCannotReset() throws Exception {
mockMvc.perform(post("/api/v1/wallet-runtime/reset").with(role(4)))
.andExpect(status().isForbidden());
mockMvc.perform(post("/api/v1/wallet-runtime/reset").with(role(1)))
.andExpect(status().isOk());
}
private int userCount(String table) {
Integer value = jdbc.queryForObject("SELECT COUNT(*) FROM " + table + " WHERE user_id=?",
Integer.class, USER_ID);
return value == null ? 0 : value;
}
private RequestPostProcessor role(int roleId) {
return jwt().jwt(jwt -> jwt.subject(USER_ID).claim("userId", USER_ID)
.claim("preferred_username", "runtime-reset-test")
.claim("schoolId", String.valueOf(SCHOOL_ID)).claim("classId", String.valueOf(CLASS_ID))
.claim("roleId", roleId));
}
}
Loading…
Cancel
Save