后台子币商权限相关
This commit is contained in:
parent
5e9ed4c0c5
commit
50da0a569b
@ -1577,7 +1577,7 @@
|
||||
"operationId": "listCoinSellers",
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/EmptyResponse"
|
||||
"$ref": "#/components/responses/CoinSellerPageResponse"
|
||||
}
|
||||
},
|
||||
"x-permission": "coin-seller:view",
|
||||
@ -1585,9 +1585,12 @@
|
||||
},
|
||||
"post": {
|
||||
"operationId": "createCoinSeller",
|
||||
"requestBody": {
|
||||
"$ref": "#/components/requestBodies/CreateCoinSellerRequest"
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/EmptyResponse"
|
||||
"201": {
|
||||
"$ref": "#/components/responses/CoinSellerResponse"
|
||||
}
|
||||
},
|
||||
"x-permission": "coin-seller:create",
|
||||
@ -1597,9 +1600,12 @@
|
||||
"/admin/coin-sellers/{user_id}/status": {
|
||||
"patch": {
|
||||
"operationId": "setCoinSellerStatus",
|
||||
"requestBody": {
|
||||
"$ref": "#/components/requestBodies/CoinSellerStatusRequest"
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/EmptyResponse"
|
||||
"$ref": "#/components/responses/CoinSellerResponse"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -1770,9 +1776,18 @@
|
||||
"/admin/countries": {
|
||||
"get": {
|
||||
"operationId": "listCountries",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "enabled",
|
||||
"schema": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/EmptyResponse"
|
||||
"$ref": "#/components/responses/CountryListResponse"
|
||||
}
|
||||
},
|
||||
"x-permission": "country:view",
|
||||
@ -1780,9 +1795,12 @@
|
||||
},
|
||||
"post": {
|
||||
"operationId": "createCountry",
|
||||
"requestBody": {
|
||||
"$ref": "#/components/requestBodies/CountryCreateRequest"
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/EmptyResponse"
|
||||
"201": {
|
||||
"$ref": "#/components/responses/CountryResponse"
|
||||
}
|
||||
},
|
||||
"x-permission": "country:create",
|
||||
@ -1794,7 +1812,7 @@
|
||||
"operationId": "getCountry",
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/EmptyResponse"
|
||||
"$ref": "#/components/responses/CountryResponse"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -1812,9 +1830,12 @@
|
||||
},
|
||||
"patch": {
|
||||
"operationId": "updateCountry",
|
||||
"requestBody": {
|
||||
"$ref": "#/components/requestBodies/CountryUpdateRequest"
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/EmptyResponse"
|
||||
"$ref": "#/components/responses/CountryResponse"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -1834,7 +1855,7 @@
|
||||
"operationId": "deleteCountry",
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/EmptyResponse"
|
||||
"$ref": "#/components/responses/CountryResponse"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -1851,12 +1872,37 @@
|
||||
"x-permissions": ["country:status"]
|
||||
}
|
||||
},
|
||||
"/admin/countries/{country_id}/rename-code": {
|
||||
"post": {
|
||||
"operationId": "renameCountryCode",
|
||||
"requestBody": {
|
||||
"$ref": "#/components/requestBodies/CountryRenameCodeRequest"
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"$ref": "#/components/responses/CountryCodeRenameJobResponse"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "country_id",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
],
|
||||
"x-permission": "country:update",
|
||||
"x-permissions": ["country:update"]
|
||||
}
|
||||
},
|
||||
"/admin/countries/{country_id}/disable": {
|
||||
"post": {
|
||||
"operationId": "disableCountry",
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/EmptyResponse"
|
||||
"$ref": "#/components/responses/CountryResponse"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -1878,7 +1924,7 @@
|
||||
"operationId": "enableCountry",
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/EmptyResponse"
|
||||
"$ref": "#/components/responses/CountryResponse"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
@ -6038,6 +6084,70 @@
|
||||
"x-permissions": ["user:export"]
|
||||
}
|
||||
},
|
||||
"/jobs": {
|
||||
"get": {
|
||||
"operationId": "listJobs",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/Page"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/parameters/PageSize"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/parameters/Keyword"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "status",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/AdminJobPageResponse"
|
||||
}
|
||||
},
|
||||
"x-permission": "job:view",
|
||||
"x-permissions": ["job:view"]
|
||||
}
|
||||
},
|
||||
"/jobs/{id}": {
|
||||
"get": {
|
||||
"operationId": "getJob",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/Id"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/AdminJobResponse"
|
||||
}
|
||||
},
|
||||
"x-permission": "job:view",
|
||||
"x-permissions": ["job:view", "country:update"]
|
||||
}
|
||||
},
|
||||
"/jobs/{id}/cancel": {
|
||||
"post": {
|
||||
"operationId": "cancelJob",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "#/components/parameters/Id"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/EmptyResponse"
|
||||
}
|
||||
},
|
||||
"x-permission": "job:cancel",
|
||||
"x-permissions": ["job:cancel"]
|
||||
}
|
||||
},
|
||||
"/teams": {
|
||||
"get": {
|
||||
"operationId": "listTeams",
|
||||
@ -6398,6 +6508,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateCoinSellerRequest": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateCoinSellerPayload"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CoinSellerStatusRequest": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CoinSellerStatusPayload"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CountryCreateRequest": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CountryCreateInput"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CountryRenameCodeRequest": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CountryRenameCodeInput"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CountryUpdateRequest": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CountryUpdateInput"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ChangePasswordRequest": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
@ -6681,6 +6841,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"CoinSellerPageResponse": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseCoinSellerPage"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CoinSellerResponse": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseCoinSeller"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"BDProfilePageResponse": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
@ -6691,6 +6871,56 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"AdminJobPageResponse": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseAdminJobPage"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"AdminJobResponse": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseAdminJob"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CountryCodeRenameJobResponse": {
|
||||
"description": "Created",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseCountryCodeRenameJob"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CountryListResponse": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseCountryList"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CountryResponse": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ApiResponseCountry"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateUserResponse": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
@ -7004,6 +7234,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApiPageAdminJob": {
|
||||
"type": "object",
|
||||
"required": ["items", "page", "pageSize", "total"],
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/AdminJob"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"type": "integer"
|
||||
},
|
||||
"pageSize": {
|
||||
"type": "integer"
|
||||
},
|
||||
"total": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApiPageManager": {
|
||||
"type": "object",
|
||||
"required": ["items", "page", "pageSize", "total"],
|
||||
@ -7025,6 +7276,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApiPageCoinSeller": {
|
||||
"type": "object",
|
||||
"required": ["items", "page", "pageSize", "total"],
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/CoinSeller"
|
||||
}
|
||||
},
|
||||
"page": {
|
||||
"type": "integer"
|
||||
},
|
||||
"pageSize": {
|
||||
"type": "integer"
|
||||
},
|
||||
"total": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApiPageBDProfile": {
|
||||
"type": "object",
|
||||
"required": ["items", "page", "pageSize", "total"],
|
||||
@ -7351,6 +7623,36 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ApiResponseCoinSellerPage": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Envelope"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/ApiPageCoinSeller"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"ApiResponseCoinSeller": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Envelope"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/CoinSeller"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"ApiResponseBDProfilePage": {
|
||||
"allOf": [
|
||||
{
|
||||
@ -7366,6 +7668,81 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ApiResponseAdminJob": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Envelope"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/AdminJob"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"ApiResponseAdminJobPage": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Envelope"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/ApiPageAdminJob"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"ApiResponseCountry": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Envelope"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/Country"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"ApiResponseCountryCodeRenameJob": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Envelope"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/CountryCodeRenameJob"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"ApiResponseCountryList": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Envelope"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/CountryList"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"ApiResponseDashboardOverview": {
|
||||
"allOf": [
|
||||
{
|
||||
@ -7950,6 +8327,113 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"CoinSeller": {
|
||||
"type": "object",
|
||||
"required": ["userId"],
|
||||
"properties": {
|
||||
"avatar": {
|
||||
"type": "string"
|
||||
},
|
||||
"canManageSubCoinSellers": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"contact": {
|
||||
"type": "string"
|
||||
},
|
||||
"createdAtMs": {
|
||||
"type": "integer"
|
||||
},
|
||||
"createdByUserId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"displayUserId": {
|
||||
"type": "string"
|
||||
},
|
||||
"merchantAssetType": {
|
||||
"type": "string"
|
||||
},
|
||||
"merchantBalance": {
|
||||
"type": "integer"
|
||||
},
|
||||
"prettyDisplayUserId": {
|
||||
"type": "string"
|
||||
},
|
||||
"prettyId": {
|
||||
"type": "string"
|
||||
},
|
||||
"regionId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"regionName": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["active", "disabled"]
|
||||
},
|
||||
"totalRechargeUsdtMicro": {
|
||||
"type": "integer"
|
||||
},
|
||||
"updatedAtMs": {
|
||||
"type": "integer"
|
||||
},
|
||||
"userId": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateCoinSellerPayload": {
|
||||
"type": "object",
|
||||
"required": ["commandId", "targetUserId"],
|
||||
"properties": {
|
||||
"canManageSubCoinSellers": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"commandId": {
|
||||
"type": "string",
|
||||
"maxLength": 120
|
||||
},
|
||||
"contact": {
|
||||
"type": "string",
|
||||
"maxLength": 128
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"maxLength": 200
|
||||
},
|
||||
"targetUserId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CoinSellerStatusPayload": {
|
||||
"type": "object",
|
||||
"required": ["commandId", "status"],
|
||||
"properties": {
|
||||
"canManageSubCoinSellers": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"commandId": {
|
||||
"type": "string",
|
||||
"maxLength": 120
|
||||
},
|
||||
"contact": {
|
||||
"type": "string",
|
||||
"maxLength": 128
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"maxLength": 200
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["active", "disabled"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"CreateManagerPayload": {
|
||||
"type": "object",
|
||||
"required": ["targetUserId"],
|
||||
@ -8345,6 +8829,206 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"AdminJob": {
|
||||
"type": "object",
|
||||
"required": ["id", "type", "status"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"payloadJson": {
|
||||
"type": "string"
|
||||
},
|
||||
"resultJson": {
|
||||
"type": "string"
|
||||
},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"attempt": {
|
||||
"type": "integer"
|
||||
},
|
||||
"maxAttempts": {
|
||||
"type": "integer"
|
||||
},
|
||||
"artifactPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"createdBy": {
|
||||
"type": "integer"
|
||||
},
|
||||
"createdByName": {
|
||||
"type": "string"
|
||||
},
|
||||
"startedAtMs": {
|
||||
"nullable": true,
|
||||
"type": "integer"
|
||||
},
|
||||
"finishedAtMs": {
|
||||
"nullable": true,
|
||||
"type": "integer"
|
||||
},
|
||||
"createdAtMs": {
|
||||
"type": "integer"
|
||||
},
|
||||
"updatedAtMs": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Country": {
|
||||
"type": "object",
|
||||
"required": ["countryCode", "countryId"],
|
||||
"properties": {
|
||||
"countryCode": {
|
||||
"type": "string"
|
||||
},
|
||||
"countryDisplayName": {
|
||||
"type": "string"
|
||||
},
|
||||
"countryId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"countryName": {
|
||||
"type": "string"
|
||||
},
|
||||
"createdAtMs": {
|
||||
"type": "integer"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"flag": {
|
||||
"type": "string"
|
||||
},
|
||||
"isoAlpha3": {
|
||||
"type": "string"
|
||||
},
|
||||
"isoNumeric": {
|
||||
"type": "string"
|
||||
},
|
||||
"phoneCountryCode": {
|
||||
"type": "string"
|
||||
},
|
||||
"sortOrder": {
|
||||
"type": "integer"
|
||||
},
|
||||
"updatedAtMs": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CountryBaseInput": {
|
||||
"type": "object",
|
||||
"required": ["countryDisplayName", "countryName"],
|
||||
"properties": {
|
||||
"countryDisplayName": {
|
||||
"type": "string"
|
||||
},
|
||||
"countryName": {
|
||||
"type": "string"
|
||||
},
|
||||
"flag": {
|
||||
"type": "string"
|
||||
},
|
||||
"isoAlpha3": {
|
||||
"type": "string"
|
||||
},
|
||||
"isoNumeric": {
|
||||
"type": "string"
|
||||
},
|
||||
"phoneCountryCode": {
|
||||
"type": "string"
|
||||
},
|
||||
"sortOrder": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CountryCreateInput": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/CountryBaseInput"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["countryCode"],
|
||||
"properties": {
|
||||
"countryCode": {
|
||||
"type": "string"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"CountryUpdateInput": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/CountryBaseInput"
|
||||
}
|
||||
]
|
||||
},
|
||||
"CountryRenameCodeInput": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/CountryBaseInput"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["countryCode"],
|
||||
"properties": {
|
||||
"countryCode": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"CountryCodeRenameJob": {
|
||||
"type": "object",
|
||||
"required": ["jobId", "status", "countryId", "oldCountryCode", "newCountryCode"],
|
||||
"properties": {
|
||||
"jobId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"status": {
|
||||
"type": "string"
|
||||
},
|
||||
"countryId": {
|
||||
"type": "integer"
|
||||
},
|
||||
"oldCountryCode": {
|
||||
"type": "string"
|
||||
},
|
||||
"newCountryCode": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CountryList": {
|
||||
"type": "object",
|
||||
"required": ["items", "total"],
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Country"
|
||||
}
|
||||
},
|
||||
"total": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Log": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
|
||||
@ -6,6 +6,8 @@ export const PERMISSIONS = {
|
||||
userStatus: "user:status",
|
||||
userResetPassword: "user:reset-password",
|
||||
userExport: "user:export",
|
||||
jobView: "job:view",
|
||||
jobCancel: "job:cancel",
|
||||
teamView: "team:view",
|
||||
teamCreate: "team:create",
|
||||
teamUpdate: "team:update",
|
||||
|
||||
@ -18,6 +18,8 @@ import {
|
||||
listHosts,
|
||||
listManagers,
|
||||
listRegions,
|
||||
getJob,
|
||||
renameCountryCode,
|
||||
replaceRegionCountries,
|
||||
setCoinSellerStatus,
|
||||
updateBDLeaderPositionAlias,
|
||||
@ -62,7 +64,13 @@ test("host org list APIs use generated admin paths and filters", async () => {
|
||||
region_id: 7,
|
||||
status: "active",
|
||||
});
|
||||
await createManager({ canBlockUser: false, canGrantBadge: false, canGrantVip: true, contact: "+63", targetUserId: "1003" });
|
||||
await createManager({
|
||||
canBlockUser: false,
|
||||
canGrantBadge: false,
|
||||
canGrantVip: true,
|
||||
contact: "+63",
|
||||
targetUserId: "1003",
|
||||
});
|
||||
await updateManager("1003", {
|
||||
canBlockUser: false,
|
||||
canGrantVehicle: true,
|
||||
@ -212,6 +220,13 @@ test("country and region APIs use generated admin paths", async () => {
|
||||
enabled: true,
|
||||
});
|
||||
await updateCountry(12, { countryDisplayName: "United States", countryName: "United States" });
|
||||
await renameCountryCode(12, {
|
||||
countryCode: "USA",
|
||||
countryDisplayName: "United States",
|
||||
countryName: "United States",
|
||||
sortOrder: 10,
|
||||
});
|
||||
await getJob(99);
|
||||
await listRegions({ status: "active" });
|
||||
await createRegion({ countries: ["US"], name: "North America", regionCode: "NA" });
|
||||
await replaceRegionCountries(7, { countries: ["US", "CA"] });
|
||||
@ -220,10 +235,12 @@ test("country and region APIs use generated admin paths", async () => {
|
||||
const [countryListUrl, countryListInit] = vi.mocked(fetch).mock.calls[0];
|
||||
const [countryCreateUrl, countryCreateInit] = vi.mocked(fetch).mock.calls[1];
|
||||
const [countryUpdateUrl, countryUpdateInit] = vi.mocked(fetch).mock.calls[2];
|
||||
const [regionListUrl, regionListInit] = vi.mocked(fetch).mock.calls[3];
|
||||
const [regionCreateUrl, regionCreateInit] = vi.mocked(fetch).mock.calls[4];
|
||||
const [regionCountriesUrl, regionCountriesInit] = vi.mocked(fetch).mock.calls[5];
|
||||
const [regionDisableUrl, regionDisableInit] = vi.mocked(fetch).mock.calls[6];
|
||||
const [countryRenameUrl, countryRenameInit] = vi.mocked(fetch).mock.calls[3];
|
||||
const [jobUrl, jobInit] = vi.mocked(fetch).mock.calls[4];
|
||||
const [regionListUrl, regionListInit] = vi.mocked(fetch).mock.calls[5];
|
||||
const [regionCreateUrl, regionCreateInit] = vi.mocked(fetch).mock.calls[6];
|
||||
const [regionCountriesUrl, regionCountriesInit] = vi.mocked(fetch).mock.calls[7];
|
||||
const [regionDisableUrl, regionDisableInit] = vi.mocked(fetch).mock.calls[8];
|
||||
|
||||
expect(String(countryListUrl)).toContain("/api/v1/admin/countries?");
|
||||
expect(String(countryListUrl)).toContain("enabled=true");
|
||||
@ -232,6 +249,12 @@ test("country and region APIs use generated admin paths", async () => {
|
||||
expect(countryCreateInit?.method).toBe("POST");
|
||||
expect(String(countryUpdateUrl)).toContain("/api/v1/admin/countries/12");
|
||||
expect(countryUpdateInit?.method).toBe("PATCH");
|
||||
expect(JSON.parse(String(countryUpdateInit?.body))).not.toHaveProperty("countryCode");
|
||||
expect(String(countryRenameUrl)).toContain("/api/v1/admin/countries/12/rename-code");
|
||||
expect(countryRenameInit?.method).toBe("POST");
|
||||
expect(JSON.parse(String(countryRenameInit?.body))).toMatchObject({ countryCode: "USA", sortOrder: 10 });
|
||||
expect(String(jobUrl)).toContain("/api/v1/jobs/99");
|
||||
expect(jobInit?.method).toBe("GET");
|
||||
expect(String(regionListUrl)).toContain("/api/v1/admin/regions?");
|
||||
expect(String(regionListUrl)).toContain("status=active");
|
||||
expect(regionListInit?.method).toBe("GET");
|
||||
|
||||
@ -6,6 +6,7 @@ import type {
|
||||
AgencyHostAddPayload,
|
||||
AgencyJoinEnabledPayload,
|
||||
AgencyStatusPayload,
|
||||
AdminJobDto,
|
||||
ApiList,
|
||||
ApiPage,
|
||||
BDLeaderPositionAliasPayload,
|
||||
@ -19,8 +20,10 @@ import type {
|
||||
CoinSellerStockDebitDto,
|
||||
CoinSellerStockDebitPayload,
|
||||
CoinSellerStatusPayload,
|
||||
CountryCodeRenameJobDto,
|
||||
CountryDto,
|
||||
CountryPayload,
|
||||
CountryRenameCodePayload,
|
||||
CountryUpdatePayload,
|
||||
CreateAgencyPayload,
|
||||
CreateAgencyResultDto,
|
||||
@ -78,6 +81,27 @@ export function updateCountry(countryId: EntityId, payload: CountryUpdatePayload
|
||||
);
|
||||
}
|
||||
|
||||
export function renameCountryCode(
|
||||
countryId: EntityId,
|
||||
payload: CountryRenameCodePayload,
|
||||
): Promise<CountryCodeRenameJobDto> {
|
||||
const endpoint = API_ENDPOINTS.renameCountryCode;
|
||||
return apiRequest<CountryCodeRenameJobDto, CountryRenameCodePayload>(
|
||||
apiEndpointPath(API_OPERATIONS.renameCountryCode, { country_id: countryId }),
|
||||
{
|
||||
body: payload,
|
||||
method: endpoint.method,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function getJob(jobId: EntityId): Promise<AdminJobDto> {
|
||||
const endpoint = API_ENDPOINTS.getJob;
|
||||
return apiRequest<AdminJobDto>(apiEndpointPath(API_OPERATIONS.getJob, { id: jobId }), {
|
||||
method: endpoint.method,
|
||||
});
|
||||
}
|
||||
|
||||
export function enableCountry(countryId: EntityId): Promise<CountryDto> {
|
||||
const endpoint = API_ENDPOINTS.enableCountry;
|
||||
return apiRequest<CountryDto>(apiEndpointPath(API_OPERATIONS.enableCountry, { country_id: countryId }), {
|
||||
@ -192,10 +216,13 @@ export function createManager(payload: CreateManagerPayload): Promise<ManagerDto
|
||||
|
||||
export function updateManager(userId: EntityId, payload: UpdateManagerPayload): Promise<ManagerDto> {
|
||||
const endpoint = API_ENDPOINTS.updateManager;
|
||||
return apiRequest<ManagerDto, UpdateManagerPayload>(apiEndpointPath(API_OPERATIONS.updateManager, { user_id: userId }), {
|
||||
body: payload,
|
||||
method: endpoint.method,
|
||||
});
|
||||
return apiRequest<ManagerDto, UpdateManagerPayload>(
|
||||
apiEndpointPath(API_OPERATIONS.updateManager, { user_id: userId }),
|
||||
{
|
||||
body: payload,
|
||||
method: endpoint.method,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function listHosts(query: PageQuery = {}): Promise<ApiPage<HostProfileDto>> {
|
||||
|
||||
@ -23,8 +23,15 @@ import {
|
||||
|
||||
const pageSize = 50;
|
||||
const emptyData = { items: [], page: 1, pageSize, total: 0 };
|
||||
const emptyCreateForm = () => ({ commandId: makeCommandId("coin-seller"), contact: "", reason: "", targetUserId: "" });
|
||||
const emptyCreateForm = () => ({
|
||||
canManageSubCoinSellers: true,
|
||||
commandId: makeCommandId("coin-seller"),
|
||||
contact: "",
|
||||
reason: "",
|
||||
targetUserId: "",
|
||||
});
|
||||
const emptyEditForm = () => ({
|
||||
canManageSubCoinSellers: true,
|
||||
commandId: makeCommandId("coin-seller-status"),
|
||||
contact: "",
|
||||
reason: "",
|
||||
@ -150,6 +157,7 @@ export function useHostCoinSellersPage() {
|
||||
}
|
||||
setSelectedSeller(seller);
|
||||
setEditForm({
|
||||
canManageSubCoinSellers: seller.canManageSubCoinSellers === true,
|
||||
commandId: makeCommandId("coin-seller-status"),
|
||||
contact: seller.contact || "",
|
||||
reason: "update coin seller contact",
|
||||
@ -265,6 +273,7 @@ export function useHostCoinSellersPage() {
|
||||
`coin-seller-edit-${editForm.targetUserId || selectedSeller?.userId || ""}`,
|
||||
"币商已更新",
|
||||
async () => {
|
||||
// 编辑接口复用 status 命令,提交时带上权限字段,避免只改联系方式时把子币商权限丢给后端默认值处理。
|
||||
const payload = parseForm(coinSellerStatusSchema, editForm);
|
||||
const { targetUserId, ...body } = payload;
|
||||
await setCoinSellerStatus(targetUserId, body);
|
||||
@ -286,7 +295,9 @@ export function useHostCoinSellersPage() {
|
||||
const sellerID = seller.userId;
|
||||
setContactSavingIds((current) => ({ ...current, [sellerID]: true }));
|
||||
try {
|
||||
// 联系方式行内保存同样走状态接口,显式保留当前权限位,避免局部保存覆盖行级权限。
|
||||
await setCoinSellerStatus(sellerID, {
|
||||
canManageSubCoinSellers: seller.canManageSubCoinSellers === true,
|
||||
commandId: makeCommandId("coin-seller-contact"),
|
||||
contact,
|
||||
reason: "update coin seller contact",
|
||||
@ -313,6 +324,37 @@ export function useHostCoinSellersPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const toggleSubCoinSellerPermission = async (seller, nextEnabled = !seller?.canManageSubCoinSellers) => {
|
||||
if (!abilities.canUpdate || !seller?.userId) {
|
||||
return;
|
||||
}
|
||||
if ((seller.canManageSubCoinSellers === true) === nextEnabled) {
|
||||
return;
|
||||
}
|
||||
await runAction(
|
||||
`coin-seller-sub-permission-${seller.userId}`,
|
||||
nextEnabled ? "子币商权限已开启" : "子币商权限已关闭",
|
||||
async () => {
|
||||
// 子权限开关复用币商状态接口,必须保留 status/contact,防止权限更新被解释成状态重置。
|
||||
await setCoinSellerStatus(seller.userId, {
|
||||
canManageSubCoinSellers: nextEnabled,
|
||||
commandId: makeCommandId("coin-seller-sub-permission"),
|
||||
contact: seller.contact || "",
|
||||
reason: "update coin seller sub permission",
|
||||
status: seller.status || "active",
|
||||
});
|
||||
setSellerRowPatches((current) => ({
|
||||
...current,
|
||||
[seller.userId]: {
|
||||
...(current[seller.userId] || {}),
|
||||
canManageSubCoinSellers: nextEnabled,
|
||||
},
|
||||
}));
|
||||
await reload();
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const submitStockCredit = async (event) => {
|
||||
event.preventDefault();
|
||||
if (!selectedSeller?.userId) {
|
||||
@ -373,7 +415,9 @@ export function useHostCoinSellersPage() {
|
||||
return;
|
||||
}
|
||||
await runAction(`coin-seller-status-${seller.userId}`, nextEnabled ? "币商已启用" : "币商已停用", async () => {
|
||||
// 状态开关只改变启停状态,显式保留权限位,避免后端按缺省值改写子币商授权。
|
||||
await setCoinSellerStatus(seller.userId, {
|
||||
canManageSubCoinSellers: seller.canManageSubCoinSellers === true,
|
||||
commandId: makeCommandId("coin-seller-status"),
|
||||
reason: "",
|
||||
status: nextStatus,
|
||||
@ -444,6 +488,7 @@ export function useHostCoinSellersPage() {
|
||||
submitEditSeller,
|
||||
submitStockCredit,
|
||||
submitStockDebit,
|
||||
toggleSubCoinSellerPermission,
|
||||
toggleSeller,
|
||||
updateRateTier,
|
||||
userFilter,
|
||||
|
||||
@ -2,9 +2,17 @@ import { useCallback, useMemo, useState } from "react";
|
||||
import { parseForm } from "@/shared/forms/validation";
|
||||
import { useAdminQuery } from "@/shared/hooks/useAdminQuery.js";
|
||||
import { useToast } from "@/shared/ui/ToastProvider.jsx";
|
||||
import { createCountry, disableCountry, enableCountry, listCountries, updateCountry } from "@/features/host-org/api";
|
||||
import {
|
||||
createCountry,
|
||||
disableCountry,
|
||||
enableCountry,
|
||||
getJob,
|
||||
listCountries,
|
||||
renameCountryCode,
|
||||
updateCountry,
|
||||
} from "@/features/host-org/api";
|
||||
import { useCountryAbilities } from "@/features/host-org/permissions.js";
|
||||
import { countryCreateSchema, countryUpdateSchema } from "@/features/host-org/schema";
|
||||
import { countryCreateSchema, countryRenameCodeSchema, countryUpdateSchema } from "@/features/host-org/schema";
|
||||
|
||||
const emptyCountryForm = () => ({
|
||||
countryCode: "",
|
||||
@ -86,9 +94,30 @@ export function useHostCountriesPage() {
|
||||
const submitCountry = async (event) => {
|
||||
event.preventDefault();
|
||||
const isEdit = activeAction === "edit";
|
||||
await runAction(isEdit ? "country-edit" : "country-create", isEdit ? "国家已更新" : "国家已创建", async () => {
|
||||
const payload = parseForm(isEdit ? countryUpdateSchema : countryCreateSchema, countryForm);
|
||||
const data = isEdit ? await updateCountry(editingCountry.countryId, payload) : await createCountry(payload);
|
||||
if (!isEdit) {
|
||||
await runAction("country-create", "国家已创建", async () => {
|
||||
const payload = parseForm(countryCreateSchema, countryForm);
|
||||
const data = await createCountry(payload);
|
||||
setCountryForm(emptyCountryForm());
|
||||
setEditingCountry(null);
|
||||
setActiveAction("");
|
||||
await reload();
|
||||
return data;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const currentCountryCode = normalizeCountryCode(editingCountry?.countryCode);
|
||||
const nextCountryCode = normalizeCountryCode(countryForm.countryCode);
|
||||
if (currentCountryCode !== nextCountryCode) {
|
||||
await runCountryCodeRename();
|
||||
return;
|
||||
}
|
||||
|
||||
await runAction("country-edit", "国家已更新", async () => {
|
||||
const { countryCode: _countryCode, enabled: _enabled, ...formWithoutImmutableFields } = countryForm;
|
||||
const payload = parseForm(countryUpdateSchema, formWithoutImmutableFields);
|
||||
const data = await updateCountry(editingCountry.countryId, payload);
|
||||
setCountryForm(emptyCountryForm());
|
||||
setEditingCountry(null);
|
||||
setActiveAction("");
|
||||
@ -97,6 +126,26 @@ export function useHostCountriesPage() {
|
||||
});
|
||||
};
|
||||
|
||||
const runCountryCodeRename = async () => {
|
||||
setLoadingAction("country-rename");
|
||||
try {
|
||||
const payload = parseForm(countryRenameCodeSchema, countryForm);
|
||||
const job = await renameCountryCode(editingCountry.countryId, payload);
|
||||
showToast(`国家码重命名任务已创建:#${job.jobId}`, "success");
|
||||
setCountryForm(emptyCountryForm());
|
||||
setEditingCountry(null);
|
||||
setActiveAction("");
|
||||
// 国家码会同步 user/admin/room 多个当前态,只有 job succeeded 后才刷新列表,避免短暂显示新旧码混杂。
|
||||
await waitCountryCodeRenameJob(job.jobId);
|
||||
await reload();
|
||||
showToast("国家码重命名完成", "success");
|
||||
} catch (err) {
|
||||
showToast(err.message || "国家码重命名失败", "error");
|
||||
} finally {
|
||||
setLoadingAction("");
|
||||
}
|
||||
};
|
||||
|
||||
const toggleCountry = async (country, nextEnabled = !country.enabled) => {
|
||||
if (!abilities.canStatus || Boolean(country.enabled) === nextEnabled) {
|
||||
return;
|
||||
@ -144,6 +193,33 @@ export function useHostCountriesPage() {
|
||||
};
|
||||
}
|
||||
|
||||
async function waitCountryCodeRenameJob(jobId) {
|
||||
const deadline = Date.now() + 120000;
|
||||
while (Date.now() < deadline) {
|
||||
const job = await getJob(jobId);
|
||||
if (job.status === "succeeded") {
|
||||
return job;
|
||||
}
|
||||
if (job.status === "failed" || job.status === "canceled") {
|
||||
throw new Error(job.error || "国家码重命名任务失败");
|
||||
}
|
||||
await delay(1500);
|
||||
}
|
||||
throw new Error("国家码重命名任务仍在处理中,请稍后刷新查看");
|
||||
}
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((resolve) => {
|
||||
window.setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeCountryCode(value) {
|
||||
return String(value || "")
|
||||
.trim()
|
||||
.toUpperCase();
|
||||
}
|
||||
|
||||
function filterCountries(items, query) {
|
||||
const keyword = query.trim().toLowerCase();
|
||||
if (!keyword) {
|
||||
|
||||
127
src/features/host-org/hooks/useHostCountriesPage.test.jsx
Normal file
127
src/features/host-org/hooks/useHostCountriesPage.test.jsx
Normal file
@ -0,0 +1,127 @@
|
||||
import { act, renderHook } from "@testing-library/react";
|
||||
import { afterEach, expect, test, vi } from "vitest";
|
||||
import { useHostCountriesPage } from "./useHostCountriesPage.js";
|
||||
import { getJob, renameCountryCode, updateCountry } from "@/features/host-org/api";
|
||||
import { useCountryAbilities } from "@/features/host-org/permissions.js";
|
||||
import { useAdminQuery } from "@/shared/hooks/useAdminQuery.js";
|
||||
import { useToast } from "@/shared/ui/ToastProvider.jsx";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
reload: vi.fn(),
|
||||
showToast: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/features/host-org/api", () => ({
|
||||
createCountry: vi.fn(),
|
||||
disableCountry: vi.fn(),
|
||||
enableCountry: vi.fn(),
|
||||
getJob: vi.fn(),
|
||||
listCountries: vi.fn(),
|
||||
renameCountryCode: vi.fn(),
|
||||
updateCountry: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/features/host-org/permissions.js", () => ({
|
||||
useCountryAbilities: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/shared/hooks/useAdminQuery.js", () => ({
|
||||
useAdminQuery: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/shared/ui/ToastProvider.jsx", () => ({
|
||||
useToast: vi.fn(),
|
||||
}));
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test("country edit without code change uses metadata update endpoint", async () => {
|
||||
mockHookDeps();
|
||||
vi.mocked(updateCountry).mockResolvedValue({ countryId: 12, countryCode: "PH" });
|
||||
const { result } = renderHook(() => useHostCountriesPage());
|
||||
|
||||
act(() => {
|
||||
result.current.openEditCountry(countryFixture());
|
||||
});
|
||||
await act(async () => {
|
||||
await result.current.submitCountry({ preventDefault: vi.fn() });
|
||||
});
|
||||
|
||||
expect(updateCountry).toHaveBeenCalledWith(
|
||||
12,
|
||||
expect.not.objectContaining({
|
||||
countryCode: expect.anything(),
|
||||
}),
|
||||
);
|
||||
expect(renameCountryCode).not.toHaveBeenCalled();
|
||||
expect(mocks.reload).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("country edit with code change creates rename job and polls until success", async () => {
|
||||
mockHookDeps();
|
||||
vi.mocked(renameCountryCode).mockResolvedValue({
|
||||
countryId: 12,
|
||||
jobId: 99,
|
||||
newCountryCode: "PHL",
|
||||
oldCountryCode: "PH",
|
||||
status: "pending",
|
||||
});
|
||||
vi.mocked(getJob).mockResolvedValue({ id: 99, status: "succeeded", type: "country-code-rename" });
|
||||
const { result } = renderHook(() => useHostCountriesPage());
|
||||
|
||||
act(() => {
|
||||
result.current.openEditCountry(countryFixture());
|
||||
});
|
||||
act(() => {
|
||||
result.current.setCountryForm({ ...result.current.countryForm, countryCode: "phl" });
|
||||
});
|
||||
await act(async () => {
|
||||
await result.current.submitCountry({ preventDefault: vi.fn() });
|
||||
});
|
||||
|
||||
expect(renameCountryCode).toHaveBeenCalledWith(
|
||||
12,
|
||||
expect.objectContaining({
|
||||
countryCode: "PHL",
|
||||
countryDisplayName: "菲律宾",
|
||||
countryName: "Philippines",
|
||||
}),
|
||||
);
|
||||
expect(getJob).toHaveBeenCalledWith(99);
|
||||
expect(updateCountry).not.toHaveBeenCalled();
|
||||
expect(mocks.showToast).toHaveBeenCalledWith("国家码重命名完成", "success");
|
||||
expect(mocks.reload).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
function mockHookDeps() {
|
||||
vi.mocked(useCountryAbilities).mockReturnValue({
|
||||
canCreate: true,
|
||||
canStatus: true,
|
||||
canUpdate: true,
|
||||
canView: true,
|
||||
});
|
||||
vi.mocked(useToast).mockReturnValue({ showToast: mocks.showToast });
|
||||
vi.mocked(useAdminQuery).mockReturnValue({
|
||||
data: { items: [], total: 0 },
|
||||
error: null,
|
||||
loading: false,
|
||||
reload: mocks.reload,
|
||||
});
|
||||
}
|
||||
|
||||
function countryFixture() {
|
||||
return {
|
||||
countryCode: "PH",
|
||||
countryDisplayName: "菲律宾",
|
||||
countryId: 12,
|
||||
countryName: "Philippines",
|
||||
enabled: true,
|
||||
flag: "🇵🇭",
|
||||
isoAlpha3: "PHL",
|
||||
isoNumeric: "608",
|
||||
phoneCountryCode: "+63",
|
||||
sortOrder: 1770,
|
||||
};
|
||||
}
|
||||
@ -2,6 +2,7 @@ import Add from "@mui/icons-material/Add";
|
||||
import ArrowDownwardOutlined from "@mui/icons-material/ArrowDownwardOutlined";
|
||||
import ArrowUpwardOutlined from "@mui/icons-material/ArrowUpwardOutlined";
|
||||
import DeleteOutlineOutlined from "@mui/icons-material/DeleteOutlineOutlined";
|
||||
import EditOutlined from "@mui/icons-material/EditOutlined";
|
||||
import MonetizationOnOutlined from "@mui/icons-material/MonetizationOnOutlined";
|
||||
import RemoveCircleOutlineOutlined from "@mui/icons-material/RemoveCircleOutlineOutlined";
|
||||
import ReceiptLongOutlined from "@mui/icons-material/ReceiptLongOutlined";
|
||||
@ -16,6 +17,7 @@ import {
|
||||
AdminFormFieldGrid,
|
||||
AdminFormReadOnlyField,
|
||||
AdminFormSection,
|
||||
AdminFormSwitchField,
|
||||
} from "@/shared/ui/AdminFormDialog.jsx";
|
||||
import { formatMillis } from "@/shared/utils/time.js";
|
||||
import { DataState } from "@/shared/ui/DataState.jsx";
|
||||
@ -91,13 +93,19 @@ export function HostCoinSellersPage() {
|
||||
render: (item) => <SellerStatusSwitch item={item} page={page} />,
|
||||
width: "minmax(90px, 0.6fr)",
|
||||
},
|
||||
{
|
||||
key: "canManageSubCoinSellers",
|
||||
label: "子币商权限",
|
||||
render: (item) => <SubCoinSellerPermissionSwitch item={item} page={page} />,
|
||||
width: "minmax(120px, 0.7fr)",
|
||||
},
|
||||
...baseColumns.slice(2),
|
||||
page.abilities.canStockCredit || page.abilities.canLedger
|
||||
page.abilities.canUpdate || page.abilities.canStockCredit || page.abilities.canLedger
|
||||
? {
|
||||
key: "actions",
|
||||
label: "操作",
|
||||
render: (item) => <SellerActions item={item} page={page} />,
|
||||
width: "minmax(130px, 0.65fr)",
|
||||
width: "minmax(160px, 0.75fr)",
|
||||
}
|
||||
: null,
|
||||
]
|
||||
@ -167,7 +175,7 @@ export function HostCoinSellersPage() {
|
||||
columns={columns}
|
||||
context={{ page, regionOptions: page.regionOptions }}
|
||||
items={items}
|
||||
minWidth="930px"
|
||||
minWidth="1080px"
|
||||
pagination={
|
||||
total > 0
|
||||
? {
|
||||
@ -208,6 +216,14 @@ export function HostCoinSellersPage() {
|
||||
value={page.createForm.contact}
|
||||
onChange={(event) => page.setCreateForm({ ...page.createForm, contact: event.target.value })}
|
||||
/>
|
||||
<AdminFormSwitchField
|
||||
checked={page.createForm.canManageSubCoinSellers !== false}
|
||||
disabled={createDisabled}
|
||||
label="子币商权限"
|
||||
onChange={(checked) =>
|
||||
page.setCreateForm({ ...page.createForm, canManageSubCoinSellers: checked })
|
||||
}
|
||||
/>
|
||||
</HostOrgActionModal>
|
||||
|
||||
<HostOrgActionModal
|
||||
@ -226,6 +242,12 @@ export function HostCoinSellersPage() {
|
||||
value={page.editForm.contact}
|
||||
onChange={(event) => page.setEditForm({ ...page.editForm, contact: event.target.value })}
|
||||
/>
|
||||
<AdminFormSwitchField
|
||||
checked={page.editForm.canManageSubCoinSellers === true}
|
||||
disabled={updateDisabled}
|
||||
label="子币商权限"
|
||||
onChange={(checked) => page.setEditForm({ ...page.editForm, canManageSubCoinSellers: checked })}
|
||||
/>
|
||||
</HostOrgActionModal>
|
||||
|
||||
<HostOrgActionModal
|
||||
@ -482,6 +504,21 @@ function SellerStatusSwitch({ item, page }) {
|
||||
);
|
||||
}
|
||||
|
||||
function SubCoinSellerPermissionSwitch({ item, page }) {
|
||||
return (
|
||||
<AdminSwitch
|
||||
checked={item.canManageSubCoinSellers === true}
|
||||
checkedLabel="开"
|
||||
disabled={
|
||||
!page.abilities.canUpdate || page.loadingAction === `coin-seller-sub-permission-${item.userId}`
|
||||
}
|
||||
inputProps={{ "aria-label": item.canManageSubCoinSellers === true ? "关闭子币商权限" : "开启子币商权限" }}
|
||||
uncheckedLabel="关"
|
||||
onChange={(event) => page.toggleSubCoinSellerPermission(item, event.target.checked)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function SellerContact({ item, page }) {
|
||||
const contact = String(item.contact || "").trim();
|
||||
return (
|
||||
@ -505,6 +542,15 @@ function SellerContact({ item, page }) {
|
||||
function SellerActions({ item, page }) {
|
||||
return (
|
||||
<AdminRowActions>
|
||||
{page.abilities.canUpdate ? (
|
||||
<AdminActionIconButton
|
||||
disabled={page.loadingAction === `coin-seller-edit-${item.userId}`}
|
||||
label="编辑币商"
|
||||
onClick={() => page.openEditSeller(item)}
|
||||
>
|
||||
<EditOutlined fontSize="small" />
|
||||
</AdminActionIconButton>
|
||||
) : null}
|
||||
{page.abilities.canLedger ? (
|
||||
<AdminActionIconButton
|
||||
label="币商流水"
|
||||
|
||||
@ -6,6 +6,7 @@ import { useHostCoinSellersPage } from "@/features/host-org/hooks/useHostCoinSel
|
||||
const mocks = vi.hoisted(() => ({
|
||||
ledgerTable: vi.fn(),
|
||||
openSellerLedger: vi.fn(),
|
||||
toggleSubCoinSellerPermission: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/features/host-org/hooks/useHostCoinSellersPage.js", () => ({
|
||||
@ -41,9 +42,29 @@ test("coin seller table shows cumulative USDT and hides creator id column", () =
|
||||
|
||||
expect(screen.queryByText("创建人 ID")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("累充USDT")).toBeInTheDocument();
|
||||
expect(screen.getByText("子币商权限")).toBeInTheDocument();
|
||||
expect(screen.getByText("12.5")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("coin seller sub permission switch toggles through the page hook", () => {
|
||||
const seller = sellerFixture();
|
||||
vi.mocked(useHostCoinSellersPage).mockReturnValue(
|
||||
pageFixture({
|
||||
abilities: { ...pageFixture().abilities, canUpdate: true },
|
||||
data: { items: [seller], page: 1, pageSize: 50, total: 1 },
|
||||
}),
|
||||
);
|
||||
|
||||
render(<HostCoinSellersPage />);
|
||||
|
||||
const switchInput = screen.getByRole("switch", { name: "关闭子币商权限" });
|
||||
expect(switchInput).toBeChecked();
|
||||
|
||||
fireEvent.click(switchInput);
|
||||
|
||||
expect(mocks.toggleSubCoinSellerPermission).toHaveBeenCalledWith(seller, false);
|
||||
});
|
||||
|
||||
test("coin seller balance and cumulative USDT headers request sorting", () => {
|
||||
const changeSort = vi.fn();
|
||||
vi.mocked(useHostCoinSellersPage).mockReturnValue(pageFixture({ changeSort }));
|
||||
@ -86,6 +107,56 @@ test("coin seller ledger drawer passes locked seller id to shared table", () =>
|
||||
expect(mocks.ledgerTable).toHaveBeenCalledWith(expect.objectContaining({ lockedSeller: seller, sellerUserId: "3001" }));
|
||||
});
|
||||
|
||||
test("coin seller create form includes sub permission switch", () => {
|
||||
const setCreateForm = vi.fn();
|
||||
vi.mocked(useHostCoinSellersPage).mockReturnValue(
|
||||
pageFixture({
|
||||
abilities: { ...pageFixture().abilities, canCreate: true },
|
||||
activeAction: "create",
|
||||
createForm: { canManageSubCoinSellers: true, contact: "", targetUserId: "" },
|
||||
setCreateForm,
|
||||
}),
|
||||
);
|
||||
|
||||
render(<HostCoinSellersPage />);
|
||||
|
||||
const switchInput = screen.getByRole("switch", { name: "子币商权限" });
|
||||
expect(switchInput).toBeChecked();
|
||||
|
||||
fireEvent.click(switchInput);
|
||||
|
||||
expect(setCreateForm).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
canManageSubCoinSellers: false,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
test("coin seller edit form preserves sub permission field", () => {
|
||||
const setEditForm = vi.fn();
|
||||
vi.mocked(useHostCoinSellersPage).mockReturnValue(
|
||||
pageFixture({
|
||||
abilities: { ...pageFixture().abilities, canUpdate: true },
|
||||
activeAction: "edit",
|
||||
editForm: { canManageSubCoinSellers: false, contact: "+63", targetUserId: "3001" },
|
||||
setEditForm,
|
||||
}),
|
||||
);
|
||||
|
||||
render(<HostCoinSellersPage />);
|
||||
|
||||
const switchInput = screen.getByRole("switch", { name: "子币商权限" });
|
||||
expect(switchInput).not.toBeChecked();
|
||||
|
||||
fireEvent.click(switchInput);
|
||||
|
||||
expect(setEditForm).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
canManageSubCoinSellers: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
function pageFixture(patch = {}) {
|
||||
return {
|
||||
abilities: {
|
||||
@ -105,9 +176,9 @@ function pageFixture(patch = {}) {
|
||||
changeStatus: vi.fn(),
|
||||
closeAction: vi.fn(),
|
||||
contactSavingIds: {},
|
||||
createForm: { contact: "", targetUserId: "" },
|
||||
createForm: { canManageSubCoinSellers: true, contact: "", targetUserId: "" },
|
||||
data: { items: [sellerFixture()], page: 1, pageSize: 50, total: 1 },
|
||||
editForm: { contact: "", targetUserId: "" },
|
||||
editForm: { canManageSubCoinSellers: true, contact: "", targetUserId: "" },
|
||||
error: null,
|
||||
loading: false,
|
||||
loadingAction: "",
|
||||
@ -142,6 +213,7 @@ function pageFixture(patch = {}) {
|
||||
submitEditSeller: vi.fn(),
|
||||
submitRateSettings: vi.fn(),
|
||||
submitStockCredit: vi.fn(),
|
||||
toggleSubCoinSellerPermission: mocks.toggleSubCoinSellerPermission,
|
||||
toggleSeller: vi.fn(),
|
||||
updateRateTier: vi.fn(),
|
||||
...patch,
|
||||
@ -151,6 +223,7 @@ function pageFixture(patch = {}) {
|
||||
function sellerFixture() {
|
||||
return {
|
||||
avatar: "",
|
||||
canManageSubCoinSellers: true,
|
||||
contact: "菲律賓 +63",
|
||||
displayUserId: "164425",
|
||||
merchantAssetType: "COIN_SELLER_COIN",
|
||||
|
||||
@ -108,7 +108,11 @@ export function HostCountriesPage() {
|
||||
|
||||
<HostOrgActionModal
|
||||
disabled={page.activeAction === "edit" ? updateDisabled : createDisabled}
|
||||
loading={page.loadingAction === "country-create" || page.loadingAction === "country-edit"}
|
||||
loading={
|
||||
page.loadingAction === "country-create" ||
|
||||
page.loadingAction === "country-edit" ||
|
||||
page.loadingAction === "country-rename"
|
||||
}
|
||||
open={page.activeAction === "create" || page.activeAction === "edit"}
|
||||
sectionTitle="国家信息"
|
||||
onClose={page.closeAction}
|
||||
@ -116,7 +120,7 @@ export function HostCountriesPage() {
|
||||
title={page.activeAction === "edit" ? "编辑国家" : "创建国家"}
|
||||
>
|
||||
<TextField
|
||||
disabled={page.activeAction === "edit" || createDisabled}
|
||||
disabled={page.activeAction === "edit" ? updateDisabled : createDisabled}
|
||||
label="国家码"
|
||||
required
|
||||
value={page.countryForm.countryCode}
|
||||
|
||||
94
src/features/host-org/pages/HostCountriesPage.test.jsx
Normal file
94
src/features/host-org/pages/HostCountriesPage.test.jsx
Normal file
@ -0,0 +1,94 @@
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { afterEach, expect, test, vi } from "vitest";
|
||||
import { HostCountriesPage } from "./HostCountriesPage.jsx";
|
||||
import { useHostCountriesPage } from "@/features/host-org/hooks/useHostCountriesPage.js";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
setCountryForm: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/features/host-org/hooks/useHostCountriesPage.js", () => ({
|
||||
useHostCountriesPage: vi.fn(),
|
||||
}));
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test("country code field is editable when editing a country with update permission", () => {
|
||||
vi.mocked(useHostCountriesPage).mockReturnValue(pageFixture());
|
||||
|
||||
render(<HostCountriesPage />);
|
||||
|
||||
const countryCodeInput = screen.getByRole("textbox", { name: /国家码/ });
|
||||
expect(countryCodeInput).not.toBeDisabled();
|
||||
|
||||
fireEvent.change(countryCodeInput, { target: { value: "PHL" } });
|
||||
|
||||
expect(mocks.setCountryForm).toHaveBeenCalledWith(expect.objectContaining({ countryCode: "PHL" }));
|
||||
});
|
||||
|
||||
test("country code field stays disabled when editing without update permission", () => {
|
||||
vi.mocked(useHostCountriesPage).mockReturnValue(
|
||||
pageFixture({
|
||||
abilities: { ...pageFixture().abilities, canUpdate: false },
|
||||
}),
|
||||
);
|
||||
|
||||
render(<HostCountriesPage />);
|
||||
|
||||
expect(screen.getByRole("textbox", { name: /国家码/ })).toBeDisabled();
|
||||
});
|
||||
|
||||
function pageFixture(patch = {}) {
|
||||
return {
|
||||
abilities: {
|
||||
canCreate: true,
|
||||
canStatus: true,
|
||||
canUpdate: true,
|
||||
canView: true,
|
||||
},
|
||||
activeAction: "edit",
|
||||
changeEnabledStatus: vi.fn(),
|
||||
changeQuery: vi.fn(),
|
||||
closeAction: vi.fn(),
|
||||
countryForm: countryFormFixture(),
|
||||
data: { items: [], total: 0 },
|
||||
editingCountry: countryFixture(),
|
||||
enabledStatus: "",
|
||||
error: null,
|
||||
loading: false,
|
||||
loadingAction: "",
|
||||
openCreateCountry: vi.fn(),
|
||||
openEditCountry: vi.fn(),
|
||||
query: "",
|
||||
reload: vi.fn(),
|
||||
resetFilters: vi.fn(),
|
||||
setCountryForm: mocks.setCountryForm,
|
||||
submitCountry: vi.fn(),
|
||||
toggleCountry: vi.fn(),
|
||||
...patch,
|
||||
};
|
||||
}
|
||||
|
||||
function countryFormFixture() {
|
||||
return {
|
||||
countryCode: "PH",
|
||||
countryDisplayName: "菲律宾",
|
||||
countryName: "Philippines",
|
||||
enabled: true,
|
||||
flag: "🇵🇭",
|
||||
isoAlpha3: "PHL",
|
||||
isoNumeric: "608",
|
||||
phoneCountryCode: "+63",
|
||||
sortOrder: 1770,
|
||||
};
|
||||
}
|
||||
|
||||
function countryFixture() {
|
||||
return {
|
||||
...countryFormFixture(),
|
||||
countryId: 12,
|
||||
updatedAtMs: 1783578249000,
|
||||
};
|
||||
}
|
||||
@ -1,5 +1,13 @@
|
||||
import { expect, test } from "vitest";
|
||||
import { agencyCloseSchema, agencyJoinEnabledSchema, coinSellerStatusSchema, createManagerSchema } from "./schema";
|
||||
import {
|
||||
agencyCloseSchema,
|
||||
agencyJoinEnabledSchema,
|
||||
coinSellerStatusSchema,
|
||||
createCoinSellerSchema,
|
||||
createManagerSchema,
|
||||
countryRenameCodeSchema,
|
||||
countryUpdateSchema,
|
||||
} from "./schema";
|
||||
|
||||
test("coin seller contact accepts plus sign with large internal user id", () => {
|
||||
const result = coinSellerStatusSchema.safeParse({
|
||||
@ -17,6 +25,37 @@ test("coin seller contact accepts plus sign with large internal user id", () =>
|
||||
}
|
||||
});
|
||||
|
||||
test("coin seller schemas preserve sub seller permission defaults and explicit false", () => {
|
||||
const createDefault = createCoinSellerSchema.safeParse({
|
||||
commandId: "coin-seller-create-test",
|
||||
targetUserId: "1003",
|
||||
});
|
||||
const statusDefault = coinSellerStatusSchema.safeParse({
|
||||
commandId: "coin-seller-edit-test",
|
||||
status: "active",
|
||||
targetUserId: "320756743338463232",
|
||||
});
|
||||
const statusDisabled = coinSellerStatusSchema.safeParse({
|
||||
canManageSubCoinSellers: false,
|
||||
commandId: "coin-seller-edit-test",
|
||||
status: "active",
|
||||
targetUserId: "320756743338463232",
|
||||
});
|
||||
|
||||
expect(createDefault.success).toBe(true);
|
||||
expect(statusDefault.success).toBe(true);
|
||||
expect(statusDisabled.success).toBe(true);
|
||||
if (createDefault.success) {
|
||||
expect(createDefault.data.canManageSubCoinSellers).toBe(true);
|
||||
}
|
||||
if (statusDefault.success) {
|
||||
expect(statusDefault.data).not.toHaveProperty("canManageSubCoinSellers");
|
||||
}
|
||||
if (statusDisabled.success) {
|
||||
expect(statusDisabled.data.canManageSubCoinSellers).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
test("agency action schemas preserve large agency ids as strings", () => {
|
||||
const agencyId = "321170072154411009";
|
||||
const closeResult = agencyCloseSchema.safeParse({
|
||||
@ -41,6 +80,36 @@ test("agency action schemas preserve large agency ids as strings", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("country update schema excludes country code from metadata update payload", () => {
|
||||
const result = countryUpdateSchema.safeParse({
|
||||
countryCode: "phl",
|
||||
countryDisplayName: "Philippines Display",
|
||||
countryName: "Philippines",
|
||||
sortOrder: "1770",
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data).not.toHaveProperty("countryCode");
|
||||
expect(result.data).not.toHaveProperty("enabled");
|
||||
}
|
||||
});
|
||||
|
||||
test("country rename schema keeps editable country code", () => {
|
||||
const result = countryRenameCodeSchema.safeParse({
|
||||
countryCode: "phl",
|
||||
countryDisplayName: "Philippines Display",
|
||||
countryName: "Philippines",
|
||||
sortOrder: "1770",
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data.countryCode).toBe("PHL");
|
||||
expect(result.data).not.toHaveProperty("enabled");
|
||||
}
|
||||
});
|
||||
|
||||
test("manager schema defaults vip grant permission on and preserves explicit false", () => {
|
||||
const defaultResult = createManagerSchema.safeParse({ targetUserId: "1003" });
|
||||
const disabledResult = createManagerSchema.safeParse({ canGrantVip: false, targetUserId: "1003" });
|
||||
|
||||
@ -105,6 +105,7 @@ export const bdStatusSchema = commandBaseSchema.extend({
|
||||
});
|
||||
|
||||
export const createCoinSellerSchema = commandContactSchema.extend({
|
||||
canManageSubCoinSellers: z.boolean().optional().default(true),
|
||||
targetUserId: userIdSchema,
|
||||
});
|
||||
|
||||
@ -124,6 +125,7 @@ export const createManagerSchema = z.object({
|
||||
});
|
||||
|
||||
export const coinSellerStatusSchema = commandContactSchema.extend({
|
||||
canManageSubCoinSellers: z.boolean().optional(),
|
||||
status: z.enum(["active", "disabled"]),
|
||||
targetUserId: entityIdSchema,
|
||||
});
|
||||
@ -200,6 +202,10 @@ export const countryUpdateSchema = countryCreateSchema.omit({
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
export const countryRenameCodeSchema = countryCreateSchema.omit({
|
||||
enabled: true,
|
||||
});
|
||||
|
||||
export const regionCreateSchema = regionBaseSchema;
|
||||
|
||||
export const regionUpdateSchema = regionBaseSchema;
|
||||
@ -221,6 +227,7 @@ export type AgencyCloseForm = z.infer<typeof agencyCloseSchema>;
|
||||
export type AgencyJoinEnabledForm = z.infer<typeof agencyJoinEnabledSchema>;
|
||||
export type CountryCreateForm = z.infer<typeof countryCreateSchema>;
|
||||
export type CountryUpdateForm = z.infer<typeof countryUpdateSchema>;
|
||||
export type CountryRenameCodeForm = z.infer<typeof countryRenameCodeSchema>;
|
||||
export type RegionCreateForm = z.infer<typeof regionCreateSchema>;
|
||||
export type RegionUpdateForm = z.infer<typeof regionUpdateSchema>;
|
||||
export type RegionCountriesForm = z.infer<typeof regionCountriesSchema>;
|
||||
|
||||
@ -29,6 +29,7 @@ export const API_OPERATIONS = {
|
||||
batchCreateGifts: "batchCreateGifts",
|
||||
batchDeleteResources: "batchDeleteResources",
|
||||
batchUpdateUserStatus: "batchUpdateUserStatus",
|
||||
cancelJob: "cancelJob",
|
||||
cancelRoomPin: "cancelRoomPin",
|
||||
changePassword: "changePassword",
|
||||
closeAgency: "closeAgency",
|
||||
@ -124,6 +125,7 @@ export const API_OPERATIONS = {
|
||||
getFirstRechargeRewardConfig: "getFirstRechargeRewardConfig",
|
||||
getHumanRoomRobotConfig: "getHumanRoomRobotConfig",
|
||||
getInviteActivityRewardConfig: "getInviteActivityRewardConfig",
|
||||
getJob: "getJob",
|
||||
getLuckyGiftConfig: "getLuckyGiftConfig",
|
||||
getLuckyGiftDrawSummary: "getLuckyGiftDrawSummary",
|
||||
getRechargeBillOverview: "getRechargeBillOverview",
|
||||
@ -191,6 +193,7 @@ export const API_OPERATIONS = {
|
||||
listHostSalarySettlements: "listHostSalarySettlements",
|
||||
listHostWithdrawals: "listHostWithdrawals",
|
||||
listInviteActivityRewardClaims: "listInviteActivityRewardClaims",
|
||||
listJobs: "listJobs",
|
||||
listLevelConfig: "listLevelConfig",
|
||||
listLoginLogs: "listLoginLogs",
|
||||
listLuckyGiftConfigs: "listLuckyGiftConfigs",
|
||||
@ -257,6 +260,7 @@ export const API_OPERATIONS = {
|
||||
refreshGoogleRechargePaid: "refreshGoogleRechargePaid",
|
||||
rejectFinanceApplication: "rejectFinanceApplication",
|
||||
rejectFinanceWithdrawalApplication: "rejectFinanceWithdrawalApplication",
|
||||
renameCountryCode: "renameCountryCode",
|
||||
replaceCoinSellerSalaryRates: "replaceCoinSellerSalaryRates",
|
||||
replaceRegionBlocks: "replaceRegionBlocks",
|
||||
replaceRegionCountries: "replaceRegionCountries",
|
||||
@ -468,6 +472,13 @@ export const API_ENDPOINTS: Record<ApiOperationId, ApiEndpoint> = {
|
||||
permission: "user:status",
|
||||
permissions: ["user:status"]
|
||||
},
|
||||
cancelJob: {
|
||||
method: "POST",
|
||||
operationId: API_OPERATIONS.cancelJob,
|
||||
path: "/v1/jobs/{id}/cancel",
|
||||
permission: "job:cancel",
|
||||
permissions: ["job:cancel"]
|
||||
},
|
||||
cancelRoomPin: {
|
||||
method: "DELETE",
|
||||
operationId: API_OPERATIONS.cancelRoomPin,
|
||||
@ -1131,6 +1142,13 @@ export const API_ENDPOINTS: Record<ApiOperationId, ApiEndpoint> = {
|
||||
permission: "invite-activity-reward:view",
|
||||
permissions: ["invite-activity-reward:view"]
|
||||
},
|
||||
getJob: {
|
||||
method: "GET",
|
||||
operationId: API_OPERATIONS.getJob,
|
||||
path: "/v1/jobs/{id}",
|
||||
permission: "job:view",
|
||||
permissions: ["job:view","country:update"]
|
||||
},
|
||||
getLuckyGiftConfig: {
|
||||
method: "GET",
|
||||
operationId: API_OPERATIONS.getLuckyGiftConfig,
|
||||
@ -1598,6 +1616,13 @@ export const API_ENDPOINTS: Record<ApiOperationId, ApiEndpoint> = {
|
||||
permission: "invite-activity-reward:view",
|
||||
permissions: ["invite-activity-reward:view"]
|
||||
},
|
||||
listJobs: {
|
||||
method: "GET",
|
||||
operationId: API_OPERATIONS.listJobs,
|
||||
path: "/v1/jobs",
|
||||
permission: "job:view",
|
||||
permissions: ["job:view"]
|
||||
},
|
||||
listLevelConfig: {
|
||||
method: "GET",
|
||||
operationId: API_OPERATIONS.listLevelConfig,
|
||||
@ -2050,6 +2075,13 @@ export const API_ENDPOINTS: Record<ApiOperationId, ApiEndpoint> = {
|
||||
permission: "finance-withdrawal:audit",
|
||||
permissions: ["finance-withdrawal:audit","finance-application:audit"]
|
||||
},
|
||||
renameCountryCode: {
|
||||
method: "POST",
|
||||
operationId: API_OPERATIONS.renameCountryCode,
|
||||
path: "/v1/admin/countries/{country_id}/rename-code",
|
||||
permission: "country:update",
|
||||
permissions: ["country:update"]
|
||||
},
|
||||
replaceCoinSellerSalaryRates: {
|
||||
method: "PUT",
|
||||
operationId: API_OPERATIONS.replaceCoinSellerSalaryRates,
|
||||
|
||||
370
src/shared/api/generated/schema.d.ts
vendored
370
src/shared/api/generated/schema.d.ts
vendored
@ -1204,6 +1204,22 @@ export interface paths {
|
||||
patch: operations["updateCountry"];
|
||||
trace?: never;
|
||||
};
|
||||
"/admin/countries/{country_id}/rename-code": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
post: operations["renameCountryCode"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/admin/countries/{country_id}/disable": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@ -3812,6 +3828,54 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/jobs": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get: operations["listJobs"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/jobs/{id}": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get: operations["getJob"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/jobs/{id}/cancel": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
get?: never;
|
||||
put?: never;
|
||||
post: operations["cancelJob"];
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/teams": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@ -3982,12 +4046,24 @@ export interface components {
|
||||
pageSize: number;
|
||||
total: number;
|
||||
};
|
||||
ApiPageAdminJob: {
|
||||
items: components["schemas"]["AdminJob"][];
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
};
|
||||
ApiPageManager: {
|
||||
items: components["schemas"]["Manager"][];
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
};
|
||||
ApiPageCoinSeller: {
|
||||
items: components["schemas"]["CoinSeller"][];
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
};
|
||||
ApiPageBDProfile: {
|
||||
items: components["schemas"]["BDProfile"][];
|
||||
page: number;
|
||||
@ -4135,9 +4211,30 @@ export interface components {
|
||||
ApiResponseManager: components["schemas"]["Envelope"] & {
|
||||
data?: components["schemas"]["Manager"];
|
||||
};
|
||||
ApiResponseCoinSellerPage: components["schemas"]["Envelope"] & {
|
||||
data?: components["schemas"]["ApiPageCoinSeller"];
|
||||
};
|
||||
ApiResponseCoinSeller: components["schemas"]["Envelope"] & {
|
||||
data?: components["schemas"]["CoinSeller"];
|
||||
};
|
||||
ApiResponseBDProfilePage: components["schemas"]["Envelope"] & {
|
||||
data?: components["schemas"]["ApiPageBDProfile"];
|
||||
};
|
||||
ApiResponseAdminJob: components["schemas"]["Envelope"] & {
|
||||
data?: components["schemas"]["AdminJob"];
|
||||
};
|
||||
ApiResponseAdminJobPage: components["schemas"]["Envelope"] & {
|
||||
data?: components["schemas"]["ApiPageAdminJob"];
|
||||
};
|
||||
ApiResponseCountry: components["schemas"]["Envelope"] & {
|
||||
data?: components["schemas"]["Country"];
|
||||
};
|
||||
ApiResponseCountryCodeRenameJob: components["schemas"]["Envelope"] & {
|
||||
data?: components["schemas"]["CountryCodeRenameJob"];
|
||||
};
|
||||
ApiResponseCountryList: components["schemas"]["Envelope"] & {
|
||||
data?: components["schemas"]["CountryList"];
|
||||
};
|
||||
ApiResponseDashboardOverview: components["schemas"]["Envelope"] & {
|
||||
data?: components["schemas"]["DashboardOverview"];
|
||||
};
|
||||
@ -4275,6 +4372,41 @@ export interface components {
|
||||
userId: string;
|
||||
username?: string;
|
||||
};
|
||||
CoinSeller: {
|
||||
avatar?: string;
|
||||
canManageSubCoinSellers?: boolean;
|
||||
contact?: string;
|
||||
createdAtMs?: number;
|
||||
createdByUserId?: number;
|
||||
displayUserId?: string;
|
||||
merchantAssetType?: string;
|
||||
merchantBalance?: number;
|
||||
prettyDisplayUserId?: string;
|
||||
prettyId?: string;
|
||||
regionId?: number;
|
||||
regionName?: string;
|
||||
/** @enum {string} */
|
||||
status?: "active" | "disabled";
|
||||
totalRechargeUsdtMicro?: number;
|
||||
updatedAtMs?: number;
|
||||
userId: string;
|
||||
username?: string;
|
||||
};
|
||||
CreateCoinSellerPayload: {
|
||||
canManageSubCoinSellers?: boolean;
|
||||
commandId: string;
|
||||
contact?: string;
|
||||
reason?: string;
|
||||
targetUserId: string;
|
||||
};
|
||||
CoinSellerStatusPayload: {
|
||||
canManageSubCoinSellers?: boolean;
|
||||
commandId: string;
|
||||
contact?: string;
|
||||
reason?: string;
|
||||
/** @enum {string} */
|
||||
status: "active" | "disabled";
|
||||
};
|
||||
CreateManagerPayload: {
|
||||
canAddAdmin?: boolean;
|
||||
canAddBdLeader?: boolean;
|
||||
@ -4406,6 +4538,65 @@ export interface components {
|
||||
updatedByAdminId?: number;
|
||||
};
|
||||
HumanRoomRobotConfigInput: components["schemas"]["HumanRoomRobotConfig"];
|
||||
AdminJob: {
|
||||
id: number;
|
||||
type: string;
|
||||
status: string;
|
||||
payloadJson?: string;
|
||||
resultJson?: string;
|
||||
error?: string;
|
||||
attempt?: number;
|
||||
maxAttempts?: number;
|
||||
artifactPath?: string;
|
||||
createdBy?: number;
|
||||
createdByName?: string;
|
||||
startedAtMs?: number | null;
|
||||
finishedAtMs?: number | null;
|
||||
createdAtMs?: number;
|
||||
updatedAtMs?: number;
|
||||
};
|
||||
Country: {
|
||||
countryCode: string;
|
||||
countryDisplayName?: string;
|
||||
countryId: number;
|
||||
countryName?: string;
|
||||
createdAtMs?: number;
|
||||
enabled?: boolean;
|
||||
flag?: string;
|
||||
isoAlpha3?: string;
|
||||
isoNumeric?: string;
|
||||
phoneCountryCode?: string;
|
||||
sortOrder?: number;
|
||||
updatedAtMs?: number;
|
||||
};
|
||||
CountryBaseInput: {
|
||||
countryDisplayName: string;
|
||||
countryName: string;
|
||||
flag?: string;
|
||||
isoAlpha3?: string;
|
||||
isoNumeric?: string;
|
||||
phoneCountryCode?: string;
|
||||
sortOrder?: number;
|
||||
};
|
||||
CountryCreateInput: components["schemas"]["CountryBaseInput"] & {
|
||||
countryCode: string;
|
||||
enabled?: boolean;
|
||||
};
|
||||
CountryUpdateInput: components["schemas"]["CountryBaseInput"];
|
||||
CountryRenameCodeInput: components["schemas"]["CountryBaseInput"] & {
|
||||
countryCode: string;
|
||||
};
|
||||
CountryCodeRenameJob: {
|
||||
jobId: number;
|
||||
status: string;
|
||||
countryId: number;
|
||||
oldCountryCode: string;
|
||||
newCountryCode: string;
|
||||
};
|
||||
CountryList: {
|
||||
items: components["schemas"]["Country"][];
|
||||
total: number;
|
||||
};
|
||||
Log: {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
@ -4662,6 +4853,24 @@ export interface components {
|
||||
};
|
||||
};
|
||||
/** @description OK */
|
||||
CoinSellerPageResponse: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiResponseCoinSellerPage"];
|
||||
};
|
||||
};
|
||||
/** @description OK */
|
||||
CoinSellerResponse: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiResponseCoinSeller"];
|
||||
};
|
||||
};
|
||||
/** @description OK */
|
||||
BDProfilePageResponse: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
@ -4671,6 +4880,51 @@ export interface components {
|
||||
};
|
||||
};
|
||||
/** @description OK */
|
||||
AdminJobPageResponse: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiResponseAdminJobPage"];
|
||||
};
|
||||
};
|
||||
/** @description OK */
|
||||
AdminJobResponse: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiResponseAdminJob"];
|
||||
};
|
||||
};
|
||||
/** @description Created */
|
||||
CountryCodeRenameJobResponse: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiResponseCountryCodeRenameJob"];
|
||||
};
|
||||
};
|
||||
/** @description OK */
|
||||
CountryListResponse: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiResponseCountryList"];
|
||||
};
|
||||
};
|
||||
/** @description OK */
|
||||
CountryResponse: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["ApiResponseCountry"];
|
||||
};
|
||||
};
|
||||
/** @description OK */
|
||||
CreateUserResponse: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
@ -4966,6 +5220,31 @@ export interface components {
|
||||
};
|
||||
};
|
||||
};
|
||||
CreateCoinSellerRequest: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["CreateCoinSellerPayload"];
|
||||
};
|
||||
};
|
||||
CoinSellerStatusRequest: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["CoinSellerStatusPayload"];
|
||||
};
|
||||
};
|
||||
CountryCreateRequest: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["CountryCreateInput"];
|
||||
};
|
||||
};
|
||||
CountryRenameCodeRequest: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["CountryRenameCodeInput"];
|
||||
};
|
||||
};
|
||||
CountryUpdateRequest: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["CountryUpdateInput"];
|
||||
};
|
||||
};
|
||||
ChangePasswordRequest: {
|
||||
content: {
|
||||
"application/json": {
|
||||
@ -6417,7 +6696,7 @@ export interface operations {
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
200: components["responses"]["EmptyResponse"];
|
||||
200: components["responses"]["CoinSellerPageResponse"];
|
||||
};
|
||||
};
|
||||
createCoinSeller: {
|
||||
@ -6427,9 +6706,9 @@ export interface operations {
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
requestBody: components["requestBodies"]["CreateCoinSellerRequest"];
|
||||
responses: {
|
||||
200: components["responses"]["EmptyResponse"];
|
||||
201: components["responses"]["CoinSellerResponse"];
|
||||
};
|
||||
};
|
||||
setCoinSellerStatus: {
|
||||
@ -6441,9 +6720,9 @@ export interface operations {
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
requestBody: components["requestBodies"]["CoinSellerStatusRequest"];
|
||||
responses: {
|
||||
200: components["responses"]["EmptyResponse"];
|
||||
200: components["responses"]["CoinSellerResponse"];
|
||||
};
|
||||
};
|
||||
creditCoinSellerStock: {
|
||||
@ -6526,14 +6805,16 @@ export interface operations {
|
||||
};
|
||||
listCountries: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
query?: {
|
||||
enabled?: boolean;
|
||||
};
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
200: components["responses"]["EmptyResponse"];
|
||||
200: components["responses"]["CountryListResponse"];
|
||||
};
|
||||
};
|
||||
createCountry: {
|
||||
@ -6543,9 +6824,9 @@ export interface operations {
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
requestBody: components["requestBodies"]["CountryCreateRequest"];
|
||||
responses: {
|
||||
200: components["responses"]["EmptyResponse"];
|
||||
201: components["responses"]["CountryResponse"];
|
||||
};
|
||||
};
|
||||
getCountry: {
|
||||
@ -6559,7 +6840,7 @@ export interface operations {
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
200: components["responses"]["EmptyResponse"];
|
||||
200: components["responses"]["CountryResponse"];
|
||||
};
|
||||
};
|
||||
deleteCountry: {
|
||||
@ -6573,7 +6854,7 @@ export interface operations {
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
200: components["responses"]["EmptyResponse"];
|
||||
200: components["responses"]["CountryResponse"];
|
||||
};
|
||||
};
|
||||
updateCountry: {
|
||||
@ -6585,9 +6866,23 @@ export interface operations {
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
requestBody: components["requestBodies"]["CountryUpdateRequest"];
|
||||
responses: {
|
||||
200: components["responses"]["EmptyResponse"];
|
||||
200: components["responses"]["CountryResponse"];
|
||||
};
|
||||
};
|
||||
renameCountryCode: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
country_id: number;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody: components["requestBodies"]["CountryRenameCodeRequest"];
|
||||
responses: {
|
||||
201: components["responses"]["CountryCodeRenameJobResponse"];
|
||||
};
|
||||
};
|
||||
disableCountry: {
|
||||
@ -6601,7 +6896,7 @@ export interface operations {
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
200: components["responses"]["EmptyResponse"];
|
||||
200: components["responses"]["CountryResponse"];
|
||||
};
|
||||
};
|
||||
enableCountry: {
|
||||
@ -6615,7 +6910,7 @@ export interface operations {
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
200: components["responses"]["EmptyResponse"];
|
||||
200: components["responses"]["CountryResponse"];
|
||||
};
|
||||
};
|
||||
listEmojiPacks: {
|
||||
@ -9658,6 +9953,51 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
listJobs: {
|
||||
parameters: {
|
||||
query?: {
|
||||
page?: components["parameters"]["Page"];
|
||||
page_size?: components["parameters"]["PageSize"];
|
||||
keyword?: components["parameters"]["Keyword"];
|
||||
status?: string;
|
||||
};
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
200: components["responses"]["AdminJobPageResponse"];
|
||||
};
|
||||
};
|
||||
getJob: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
id: components["parameters"]["Id"];
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
200: components["responses"]["AdminJobResponse"];
|
||||
};
|
||||
};
|
||||
cancelJob: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
id: components["parameters"]["Id"];
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
200: components["responses"]["EmptyResponse"];
|
||||
};
|
||||
};
|
||||
listTeams: {
|
||||
parameters: {
|
||||
query?: {
|
||||
|
||||
@ -830,6 +830,7 @@ export interface HostProfileDto {
|
||||
|
||||
export interface CoinSellerDto {
|
||||
avatar?: string;
|
||||
canManageSubCoinSellers?: boolean;
|
||||
contact?: string;
|
||||
createdAtMs?: number;
|
||||
createdByUserId?: number;
|
||||
@ -1609,11 +1610,13 @@ export interface BDStatusPayload extends HostCommandPayload {
|
||||
}
|
||||
|
||||
export interface CreateCoinSellerPayload extends HostCommandPayload {
|
||||
canManageSubCoinSellers?: boolean;
|
||||
contact?: string;
|
||||
targetUserId: string;
|
||||
}
|
||||
|
||||
export interface CoinSellerStatusPayload extends BDStatusPayload {
|
||||
canManageSubCoinSellers?: boolean;
|
||||
contact?: string;
|
||||
}
|
||||
|
||||
@ -1652,7 +1655,7 @@ export interface CountryDto {
|
||||
}
|
||||
|
||||
export interface CountryPayload {
|
||||
countryCode?: string;
|
||||
countryCode: string;
|
||||
countryDisplayName: string;
|
||||
countryName: string;
|
||||
enabled?: boolean;
|
||||
@ -1665,6 +1668,34 @@ export interface CountryPayload {
|
||||
|
||||
export type CountryUpdatePayload = Omit<CountryPayload, "countryCode" | "enabled">;
|
||||
|
||||
export type CountryRenameCodePayload = Omit<CountryPayload, "enabled">;
|
||||
|
||||
export interface CountryCodeRenameJobDto {
|
||||
countryId: number;
|
||||
jobId: number;
|
||||
newCountryCode: string;
|
||||
oldCountryCode: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface AdminJobDto {
|
||||
artifactPath?: string;
|
||||
attempt?: number;
|
||||
createdAtMs?: number;
|
||||
createdBy?: number;
|
||||
createdByName?: string;
|
||||
error?: string;
|
||||
finishedAtMs?: number | null;
|
||||
id: number;
|
||||
maxAttempts?: number;
|
||||
payloadJson?: string;
|
||||
resultJson?: string;
|
||||
startedAtMs?: number | null;
|
||||
status: string;
|
||||
type: string;
|
||||
updatedAtMs?: number;
|
||||
}
|
||||
|
||||
export interface RegionDto {
|
||||
countries?: string[];
|
||||
createdAtMs?: number;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user