修复bug;完善错误信息提示机制

This commit is contained in:
BBIT-Kai
2026-07-10 14:06:07 +08:00
parent ee355db48e
commit 8b5e2b6a38
10 changed files with 66 additions and 23 deletions
+1
View File
@@ -3,3 +3,4 @@ web/dist/
log/ log/
server/src/main/resources/dist/ server/src/main/resources/dist/
doc/票通Demo/untitled/.idea/ doc/票通Demo/untitled/.idea/
server/.kotlin/
+1 -1
View File
@@ -1,6 +1,6 @@
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
$VERSION = "1.1" $VERSION = "1.2"
$IMAGE = "docker.bbitcn.net/bbit_invoice/server" $IMAGE = "docker.bbitcn.net/bbit_invoice/server"
@@ -6,6 +6,8 @@ enum class ErrorCode(val code: String, val message: String) {
UNAUTHORIZED("AUTH.UNAUTHORIZED", "未登录或登录已失效"), UNAUTHORIZED("AUTH.UNAUTHORIZED", "未登录或登录已失效"),
FORBIDDEN("AUTH.FORBIDDEN", "无权限访问"), FORBIDDEN("AUTH.FORBIDDEN", "无权限访问"),
USERNAME_OR_PASSWORD_INVALID("AUTH.USERNAME_OR_PASSWORD_INVALID", "用户名或密码错误"), USERNAME_OR_PASSWORD_INVALID("AUTH.USERNAME_OR_PASSWORD_INVALID", "用户名或密码错误"),
ACCOUNT_NOT_FOUND("AUTH.ACCOUNT_NOT_FOUND", "账号不存在"),
PASSWORD_INVALID("AUTH.PASSWORD_INVALID", "密码错误"),
USER_DISABLED("AUTH.USER_DISABLED", "用户已禁用"), USER_DISABLED("AUTH.USER_DISABLED", "用户已禁用"),
USER_NOT_FOUND("SYSTEM.USER_NOT_FOUND", "用户不存在"), USER_NOT_FOUND("SYSTEM.USER_NOT_FOUND", "用户不存在"),
ROLE_NOT_FOUND("SYSTEM.ROLE_NOT_FOUND", "角色不存在"), ROLE_NOT_FOUND("SYSTEM.ROLE_NOT_FOUND", "角色不存在"),
@@ -99,7 +99,7 @@ fun Route.registerUserRoutes() {
val id = parseUuid(call.parameters["id"] ?: "", "id") val id = parseUuid(call.parameters["id"] ?: "", "id")
val request = call.receive<UpdateUserStatusRequest>() val request = call.receive<UpdateUserStatusRequest>()
runCatching { runCatching {
UserService.updateStatus(id, request) UserService.updateStatus(id, currentUser.id, request)
call.respond(ok<Unit>(message = "状态更新成功")) call.respond(ok<Unit>(message = "状态更新成功"))
OperationLogService.success(call, currentUser, "UPDATE_STATUS", "更新用户状态", start.elapsedNow().inWholeMilliseconds) OperationLogService.success(call, currentUser, "UPDATE_STATUS", "更新用户状态", start.elapsedNow().inWholeMilliseconds)
}.onFailure { }.onFailure {
@@ -30,31 +30,31 @@ object AuthService {
} }
val user = dbQuery { UserDao.findByUsername(username) } ?: throw BizException( val user = dbQuery { UserDao.findByUsername(username) } ?: throw BizException(
ErrorCode.USERNAME_OR_PASSWORD_INVALID.code, ErrorCode.ACCOUNT_NOT_FOUND.code,
ErrorCode.USERNAME_OR_PASSWORD_INVALID.message, ErrorCode.ACCOUNT_NOT_FOUND.message,
HttpStatusCode.BadRequest, HttpStatusCode.BadRequest,
) )
if (!PasswordService.matches(request.password, user[com.bbit.ticket.database.system.SysUserTable.passwordHash])) { if (!PasswordService.matches(request.password, user[SysUserTable.passwordHash])) {
throw BizException( throw BizException(
ErrorCode.USERNAME_OR_PASSWORD_INVALID.code, ErrorCode.PASSWORD_INVALID.code,
ErrorCode.USERNAME_OR_PASSWORD_INVALID.message, ErrorCode.PASSWORD_INVALID.message,
HttpStatusCode.BadRequest, HttpStatusCode.BadRequest,
) )
} }
if (user[com.bbit.ticket.database.system.SysUserTable.status] != "ENABLED") { if (user[SysUserTable.status] != "ENABLED") {
throw BizException(ErrorCode.USER_DISABLED.code, ErrorCode.USER_DISABLED.message, HttpStatusCode.BadRequest) throw BizException(ErrorCode.USER_DISABLED.code, ErrorCode.USER_DISABLED.message, HttpStatusCode.BadRequest)
} }
val userId = user[com.bbit.ticket.database.system.SysUserTable.id] val userId = user[SysUserTable.id]
val roleCodes = dbQuery { UserDao.findEnabledRoleCodes(userId) } val roleCodes = dbQuery { UserDao.findEnabledRoleCodes(userId) }
val (accessToken, expiresIn) = JwtService.issueAccessToken( val (accessToken, expiresIn) = JwtService.issueAccessToken(
userId = userId.toString(), userId = userId.toString(),
username = user[com.bbit.ticket.database.system.SysUserTable.username], username = user[SysUserTable.username],
roles = roleCodes, roles = roleCodes,
tokenVersion = user[com.bbit.ticket.database.system.SysUserTable.tokenVersion], tokenVersion = user[SysUserTable.tokenVersion],
) )
dbQuery { UserDao.updateLoginInfo(userId, loginIp) } dbQuery { UserDao.updateLoginInfo(userId, loginIp) }
@@ -63,7 +63,10 @@ object UserService {
UserDao.softDelete(id) UserDao.softDelete(id)
} }
suspend fun updateStatus(id: Uuid, request: UpdateUserStatusRequest) = dbQuery { suspend fun updateStatus(id: Uuid, currentUserId: Uuid, request: UpdateUserStatusRequest) = dbQuery {
if (id == currentUserId && request.status != "ENABLED") {
throw BizException(ErrorCode.BAD_REQUEST.code, "不能禁用当前登录账号")
}
UserDao.requireActive(id) UserDao.requireActive(id)
UserDao.updateStatus(id, request.status) UserDao.updateStatus(id, request.status)
} }
@@ -4,7 +4,7 @@ import java.time.OffsetDateTime
import java.time.ZoneId import java.time.ZoneId
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
private val defaultZone: ZoneId = ZoneId.systemDefault() private val defaultZone: ZoneId = ZoneId.of("Asia/Shanghai")
private val dateTimeFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm") private val dateTimeFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
fun formatDateTime(value: OffsetDateTime?): String? { fun formatDateTime(value: OffsetDateTime?): String? {
@@ -40,7 +40,7 @@ object PTApi {
/** 发送登录短信验证码 2.4 */ /** 发送登录短信验证码 2.4 */
suspend fun sendLoginSmsCode(req: GetLoginSmsCodeRequest): LoginSmsCodeResponse = suspend fun sendLoginSmsCode(req: GetLoginSmsCodeRequest): LoginSmsCodeResponse =
PTClient.ptPost("sendLoginSmsCode.pt", req) PTClient.ptPost("sendLoginSmsCode.pt", req, successResultCodes = setOf("0000", "6666"))
/** 短信验证码登录 2.5 */ /** 短信验证码登录 2.5 */
suspend fun smsLogin(req: SmsLoginRequest): SMSLoginResponse = suspend fun smsLogin(req: SmsLoginRequest): SMSLoginResponse =
@@ -34,6 +34,7 @@ object PTClient {
val logger = LoggerFactory.getLogger(PTClient::class.java) val logger = LoggerFactory.getLogger(PTClient::class.java)
private const val CONNECT_TIMEOUT_MS = 10_000 private const val CONNECT_TIMEOUT_MS = 10_000
private const val READ_TIMEOUT_MS = 60_000 private const val READ_TIMEOUT_MS = 60_000
private const val SHANGHAI_TIME_ZONE = "Asia/Shanghai"
val wireJson = Json { val wireJson = Json {
explicitNulls = false explicitNulls = false
ignoreUnknownKeys = true ignoreUnknownKeys = true
@@ -54,7 +55,8 @@ object PTClient {
suspend inline fun <reified Req, reified Resp> ptGet( suspend inline fun <reified Req, reified Resp> ptGet(
url: String, url: String,
queryParams: Req, queryParams: Req,
headers: Map<String, String> = emptyMap() headers: Map<String, String> = emptyMap(),
successResultCodes: Set<String> = setOf("0000"),
): Resp { ): Resp {
// query -> json // query -> json
@@ -128,6 +130,7 @@ object PTClient {
message = "response content is null", message = "response content is null",
serialNo = result.serialNo serialNo = result.serialNo
) )
ensureInnerResultOk(content, successResultCodes, result.serialNo)
return myJson.decodeFromJsonElement(content) return myJson.decodeFromJsonElement(content)
} }
@@ -135,7 +138,8 @@ object PTClient {
suspend inline fun <reified Req, reified Resp> ptPost( suspend inline fun <reified Req, reified Resp> ptPost(
url: String, url: String,
body: Req, body: Req,
headers: Map<String, String> = emptyMap() headers: Map<String, String> = emptyMap(),
successResultCodes: Set<String> = setOf("0000"),
): Resp { ): Resp {
// req json // req json
val reqJson = wireJson.encodeToString(body) val reqJson = wireJson.encodeToString(body)
@@ -150,6 +154,7 @@ object PTClient {
val decrypted = disposeResponse(response) val decrypted = disposeResponse(response)
val result = myJson.decodeFromString<PTResponse<JsonElement>>(decrypted) val result = myJson.decodeFromString<PTResponse<JsonElement>>(decrypted)
logger.info("res = $result")
if (result.code != "0000") { if (result.code != "0000") {
throw PTException( throw PTException(
code = result.code, code = result.code,
@@ -157,8 +162,14 @@ object PTClient {
serialNo = result.serialNo serialNo = result.serialNo
) )
} }
logger.info("res = $result") val content = result.content
return myJson.decodeFromJsonElement<Resp>(result.content!!) ?: throw PTException(
code = "CONTENT_NULL",
message = "response content is null",
serialNo = result.serialNo
)
ensureInnerResultOk(content, successResultCodes, result.serialNo)
return myJson.decodeFromJsonElement<Resp>(content)
} }
suspend fun postJsonRaw( suspend fun postJsonRaw(
@@ -220,7 +231,7 @@ object PTClient {
@Throws(Exception::class) @Throws(Exception::class)
fun buildRequestData(content: String): String { fun buildRequestData(content: String): String {
val reqContent: String = SecurityUtil.encrypt3DES(content) ?: "" val reqContent: String = SecurityUtil.encrypt3DES(content) ?: ""
val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") val sdf = shanghaiDateFormat("yyyy-MM-dd HH:mm:ss")
val map = HashMap<String, String>() val map = HashMap<String, String>()
map["platformCode"] = Global.ptPlatformCode map["platformCode"] = Global.ptPlatformCode
map["signType"] = "RSA" map["signType"] = "RSA"
@@ -287,10 +298,36 @@ object PTClient {
fun ptDate(): String { fun ptDate(): String {
val date = Date() val date = Date()
val sdf = SimpleDateFormat("YYYYMMddHHmmss") val sdf = shanghaiDateFormat("yyyyMMddHHmmss")
val str = Global.ptPlatformAlias + sdf.format(date) + (Math.random() * 90 + 10).toInt() val str = Global.ptPlatformAlias + sdf.format(date) + (Math.random() * 90 + 10).toInt()
println(str) println(str)
return str return str
} }
@PublishedApi
internal fun ensureInnerResultOk(
content: JsonElement,
successResultCodes: Set<String>,
serialNo: String?,
) {
val contentObject = content as? JsonObject ?: return
val resultCode = contentObject.stringOrNull("resultCode") ?: return
if (resultCode !in successResultCodes) {
throw PTException(
code = resultCode,
message = contentObject.stringOrNull("resultMsg") ?: "票通业务处理失败",
serialNo = serialNo,
)
}
}
@PublishedApi
internal fun JsonObject.stringOrNull(key: String): String? =
(this[key] as? JsonPrimitive)?.content?.trim()?.takeIf { it.isNotEmpty() }
private fun shanghaiDateFormat(pattern: String): SimpleDateFormat =
SimpleDateFormat(pattern).apply {
timeZone = TimeZone.getTimeZone(SHANGHAI_TIME_ZONE)
}
} }
+3 -3
View File
@@ -1,7 +1,7 @@
<configuration> <configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%X{traceId}] %logger{40} - %msg%n</pattern> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS, Asia/Shanghai} [%thread] %-5level [%X{traceId}] %logger{40} - %msg%n</pattern>
</encoder> </encoder>
</appender> </appender>
@@ -14,10 +14,10 @@
<appender name="PT_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="PT_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>../log/pt-request.log</file> <file>../log/pt-request.log</file>
<encoder> <encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %msg%n</pattern> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS, Asia/Shanghai} %msg%n</pattern>
</encoder> </encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>../log/pt-request.%d{yyyy-MM-dd}.log</fileNamePattern> <fileNamePattern>../log/pt-request.%d{yyyy-MM-dd, Asia/Shanghai}.log</fileNamePattern>
<maxHistory>60</maxHistory> <maxHistory>60</maxHistory>
</rollingPolicy> </rollingPolicy>
</appender> </appender>