增加票通票样功能
This commit is contained in:
@@ -10,11 +10,13 @@ import com.bbit.ticket.database.piaotong.HistoryInvoiceVoucherTable
|
||||
import com.bbit.ticket.entity.common.PageResult
|
||||
import com.bbit.ticket.entity.request.AskInvoiceRequest
|
||||
import com.bbit.ticket.entity.response.GetInvoiceInfoResponse
|
||||
import com.bbit.ticket.entity.response.InvoiceDownloadUrlResponse
|
||||
import com.bbit.ticket.entity.response.InvoiceDetailGoods
|
||||
import com.bbit.ticket.entity.response.InvoiceDetailOrder
|
||||
import com.bbit.ticket.entity.response.InvoiceDetailResponse
|
||||
import com.bbit.ticket.entity.response.InvoiceDetailVoucher
|
||||
import com.bbit.ticket.entity.response.InvoiceHistoryItem
|
||||
import com.bbit.ticket.utils.Base64TextUtil
|
||||
import com.bbit.ticket.utils.formatDateTime
|
||||
import org.jetbrains.exposed.v1.core.Column
|
||||
import org.jetbrains.exposed.v1.core.Op
|
||||
@@ -332,6 +334,7 @@ object BlueInvoiceDao {
|
||||
it.setIfNotNull(HistoryInvoiceBasicTable.definedData, req.definedData)
|
||||
// 文件
|
||||
it.setIfNotNull(HistoryInvoiceBasicTable.invoiceLayoutFileType, req.invoiceLayoutFileType)
|
||||
it.setIfNotNull(HistoryInvoiceBasicTable.downloadUrl, Base64TextUtil.decodeToText(req.downloadUrl))
|
||||
// 删除标记
|
||||
it[HistoryInvoiceBasicTable.invDeletedFlag] = req.invDeletedFlag ?: "0"
|
||||
}
|
||||
@@ -348,6 +351,18 @@ object BlueInvoiceDao {
|
||||
?: throw com.bbit.ticket.entity.common.BizException("NOT_FOUND", "发票记录不存在用户信息")
|
||||
}
|
||||
|
||||
fun invoiceDownloadUrl(userId: Uuid, invoiceReqSerialNo: String): InvoiceDownloadUrlResponse? {
|
||||
val row = HistoryInvoiceBasicTable.selectAll()
|
||||
.where {
|
||||
(HistoryInvoiceBasicTable.userId eq userId) and
|
||||
(HistoryInvoiceBasicTable.invoiceReqSerialNo eq invoiceReqSerialNo) and
|
||||
HistoryInvoiceBasicTable.deletedAt.isNull()
|
||||
}
|
||||
.singleOrNull() ?: return null
|
||||
|
||||
return InvoiceDownloadUrlResponse(row[HistoryInvoiceBasicTable.downloadUrl])
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询发票完整详情(含商品明细、差额征税凭证、关联单据)
|
||||
*/
|
||||
@@ -381,7 +396,7 @@ object BlueInvoiceDao {
|
||||
invoiceAmount = row[HistoryInvoiceGoodsTable.itemAmount].toPlainString(),
|
||||
taxRateValue = row[HistoryInvoiceGoodsTable.taxRate].toPlainString(),
|
||||
taxRateAmount = row[HistoryInvoiceGoodsTable.taxRateAmount].toPlainString(),
|
||||
includeTaxFlag = false,
|
||||
includeTaxFlag = row[HistoryInvoiceGoodsTable.includeTaxFlag] == "1",
|
||||
zeroTaxFlag = row[HistoryInvoiceGoodsTable.zeroTaxFlag],
|
||||
preferentialPolicyFlag = row[HistoryInvoiceGoodsTable.preferentialPolicyFlag],
|
||||
vatSpecialManage = row[HistoryInvoiceGoodsTable.vatSpecialManage],
|
||||
|
||||
@@ -5,6 +5,7 @@ package com.bbit.ticket.dao.piaotong
|
||||
import com.bbit.ticket.database.piaotong.HistoryInvoiceBasicTable
|
||||
import com.bbit.ticket.database.piaotong.HistoryInvoiceRedTable
|
||||
import com.bbit.ticket.entity.request.QuickRedInvoiceRequest
|
||||
import com.bbit.ticket.entity.response.InvoiceDownloadUrlResponse
|
||||
import com.bbit.ticket.entity.response.RedInvoiceInfoResponse
|
||||
import org.jetbrains.exposed.v1.core.and
|
||||
import org.jetbrains.exposed.v1.core.eq
|
||||
@@ -46,6 +47,7 @@ object RedInvoiceDao {
|
||||
it[HistoryInvoiceBasicTable.invoiceKind] = req.invoiceKind
|
||||
?: blueRow?.get(HistoryInvoiceBasicTable.invoiceKind) ?: "82"
|
||||
it[HistoryInvoiceBasicTable.invoiceType] = "2" // 红票
|
||||
it[HistoryInvoiceBasicTable.redFlag] = "REDING"
|
||||
|
||||
// ---- 红冲关联 ----
|
||||
it[HistoryInvoiceBasicTable.blueInvoiceCode] = blueRow?.get(HistoryInvoiceBasicTable.invoiceCode)
|
||||
@@ -115,4 +117,17 @@ object RedInvoiceDao {
|
||||
takerEmail = row[HistoryInvoiceRedTable.takerEmail],
|
||||
)
|
||||
}
|
||||
|
||||
fun invoiceDownloadUrl(userId: Uuid, invoiceReqSerialNo: String): InvoiceDownloadUrlResponse? {
|
||||
val row = HistoryInvoiceBasicTable.selectAll()
|
||||
.where {
|
||||
(HistoryInvoiceBasicTable.userId eq userId) and
|
||||
(HistoryInvoiceBasicTable.invoiceReqSerialNo eq invoiceReqSerialNo) and
|
||||
(HistoryInvoiceBasicTable.invoiceType eq "2") and
|
||||
HistoryInvoiceBasicTable.deletedAt.isNull()
|
||||
}
|
||||
.singleOrNull() ?: return null
|
||||
|
||||
return InvoiceDownloadUrlResponse(row[HistoryInvoiceBasicTable.downloadUrl])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,6 +423,7 @@ object HistoryInvoiceBasicTable : Table("history_invoice_basic") {
|
||||
* 1:已删除
|
||||
*/
|
||||
val invDeletedFlag = varchar("inv_deleted_flag", 1)
|
||||
val downloadUrl = text("download_url").nullable()
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 时间字段
|
||||
|
||||
+5
-1
@@ -130,7 +130,11 @@ object HistoryInvoiceGoodsTable : Table("history_invoice_goods") {
|
||||
* 税额
|
||||
*/
|
||||
val taxRateAmount = decimal("tax_rate_amount", 18, 2)
|
||||
|
||||
/**
|
||||
* 税额
|
||||
*/
|
||||
val includeTaxFlag = varchar("include_tax_flag", 1)
|
||||
.nullable()
|
||||
/**
|
||||
* 扣除额
|
||||
*
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.bbit.ticket.entity.response
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class InvoiceDownloadUrlResponse(
|
||||
val downloadUrl: String? = null
|
||||
)
|
||||
@@ -12,8 +12,12 @@ import com.bbit.ticket.entity.request.RedCreateRequest
|
||||
import com.bbit.ticket.service.piaotong.PTBlueService
|
||||
import com.bbit.ticket.service.piaotong.PTRedService
|
||||
import com.bbit.ticket.utils.requireCurrentUser
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.HttpHeaders
|
||||
import io.ktor.server.request.receive
|
||||
import io.ktor.server.response.header
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.response.respondBytes
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.get
|
||||
import io.ktor.server.routing.post
|
||||
@@ -93,6 +97,86 @@ fun Route.registerPTiInvoiceRoutes() {
|
||||
call.respond(fail(code = "-1", message = e.message ?: "查询发票详情失败"))
|
||||
}
|
||||
}
|
||||
get("/invoiceDownloadUrl") {
|
||||
try {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
val invoiceReqSerialNo = call.request.queryParameters["invoiceReqSerialNo"]
|
||||
if (invoiceReqSerialNo.isNullOrBlank()) {
|
||||
call.respond(fail(code = "-1", message = "璇蜂紶鍏ュ彂绁ㄨ姹傛祦姘村彿"))
|
||||
return@get
|
||||
}
|
||||
val response = PTBlueService.getInvoiceDownloadUrl(currentUser.id, invoiceReqSerialNo)
|
||||
if (response == null) {
|
||||
call.respond(fail(code = "-1", message = "鏈壘鍒拌鍙戠エ璁板綍"))
|
||||
return@get
|
||||
}
|
||||
call.respond(ok(response))
|
||||
} catch (e: Exception) {
|
||||
call.respond(fail(code = "-1", message = e.message ?: "鏌ヨ鍙戠エ涓嬭浇鍦板潃澶辫触"))
|
||||
}
|
||||
}
|
||||
get("/invoicePreview") {
|
||||
try {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
val invoiceReqSerialNo = call.request.queryParameters["invoiceReqSerialNo"]
|
||||
if (invoiceReqSerialNo.isNullOrBlank()) {
|
||||
call.respond(fail(code = "-1", message = "璇蜂紶鍏ュ彂绁ㄨ姹傛祦姘村彿"))
|
||||
return@get
|
||||
}
|
||||
val bytes = PTBlueService.getInvoicePreview(currentUser.id, invoiceReqSerialNo)
|
||||
if (bytes == null) {
|
||||
call.respond(fail(code = "-1", message = "鏈壘鍒扮エ鏍峰湴鍧€"))
|
||||
return@get
|
||||
}
|
||||
call.response.header(
|
||||
HttpHeaders.ContentDisposition,
|
||||
"inline; filename=\"${invoiceReqSerialNo}.pdf\""
|
||||
)
|
||||
call.respondBytes(bytes, ContentType.Application.Pdf)
|
||||
} catch (e: Exception) {
|
||||
call.respond(fail(code = "-1", message = e.message ?: "棰勮绁ㄦ牱澶辫触"))
|
||||
}
|
||||
}
|
||||
get("/redInvoiceDownloadUrl") {
|
||||
try {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
val invoiceReqSerialNo = call.request.queryParameters["invoiceReqSerialNo"]
|
||||
if (invoiceReqSerialNo.isNullOrBlank()) {
|
||||
call.respond(fail(code = "-1", message = "请传入发票请求流水号"))
|
||||
return@get
|
||||
}
|
||||
val response = PTRedService.getRedInvoiceDownloadUrl(currentUser.id, invoiceReqSerialNo)
|
||||
if (response == null) {
|
||||
call.respond(fail(code = "-1", message = "未找到该红票记录"))
|
||||
return@get
|
||||
}
|
||||
call.respond(ok(response))
|
||||
} catch (e: Exception) {
|
||||
call.respond(fail(code = "-1", message = e.message ?: "查询红票下载地址失败"))
|
||||
}
|
||||
}
|
||||
get("/redInvoicePreview") {
|
||||
try {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
val invoiceReqSerialNo = call.request.queryParameters["invoiceReqSerialNo"]
|
||||
if (invoiceReqSerialNo.isNullOrBlank()) {
|
||||
call.respond(fail(code = "-1", message = "请传入发票请求流水号"))
|
||||
return@get
|
||||
}
|
||||
val bytes = PTRedService.getRedInvoicePreview(currentUser.id, invoiceReqSerialNo)
|
||||
if (bytes == null) {
|
||||
call.respond(fail(code = "-1", message = "未找到票样地址"))
|
||||
return@get
|
||||
}
|
||||
call.response.header(
|
||||
HttpHeaders.ContentDisposition,
|
||||
"inline; filename=\"${invoiceReqSerialNo}.pdf\""
|
||||
)
|
||||
call.respondBytes(bytes, ContentType.Application.Pdf)
|
||||
} catch (e: Exception) {
|
||||
call.respond(fail(code = "-1", message = e.message ?: "预览红票票样失败"))
|
||||
}
|
||||
}
|
||||
get("/queryInvoice") {
|
||||
try {
|
||||
val currentUser = call.requireCurrentUser()
|
||||
|
||||
@@ -7,10 +7,13 @@ import com.bbit.ticket.entity.common.PageResult
|
||||
import com.bbit.ticket.entity.request.AskInvoiceRequest
|
||||
import com.bbit.ticket.entity.request.QueryInvoiceRequest
|
||||
import com.bbit.ticket.entity.response.GetInvoiceInfoResponse
|
||||
import com.bbit.ticket.entity.response.InvoiceDownloadUrlResponse
|
||||
import com.bbit.ticket.entity.response.QueryInvoiceResult
|
||||
import com.bbit.ticket.entity.response.InvoiceCreateResponse
|
||||
import com.bbit.ticket.entity.response.InvoiceDetailResponse
|
||||
import com.bbit.ticket.entity.response.InvoiceHistoryItem
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.bodyAsBytes
|
||||
import com.bbit.ticket.utils.plugins.dbQuery
|
||||
import com.bbit.ticket.utils.net.PTClient
|
||||
import kotlin.uuid.Uuid
|
||||
@@ -70,6 +73,16 @@ object PTBlueService {
|
||||
suspend fun getInvoiceDetail(userId: Uuid, invoiceReqSerialNo: String): InvoiceDetailResponse? =
|
||||
dbQuery { BlueInvoiceDao.invoiceDetail(userId, invoiceReqSerialNo) }
|
||||
|
||||
suspend fun getInvoiceDownloadUrl(userId: Uuid, invoiceReqSerialNo: String): InvoiceDownloadUrlResponse? =
|
||||
dbQuery { BlueInvoiceDao.invoiceDownloadUrl(userId, invoiceReqSerialNo) }
|
||||
|
||||
suspend fun getInvoicePreview(userId: Uuid, invoiceReqSerialNo: String): ByteArray? {
|
||||
val downloadUrl = getInvoiceDownloadUrl(userId, invoiceReqSerialNo)?.downloadUrl
|
||||
?.takeIf { it.isNotBlank() }
|
||||
?: return null
|
||||
return PTClient.client.get(downloadUrl).bodyAsBytes()
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询并更新发票状态(复用 syncInvoiceFromPT)
|
||||
*/
|
||||
@@ -81,4 +94,4 @@ object PTBlueService {
|
||||
return syncInvoiceFromPT(existing, invoiceReqSerialNo, req.taxpayerNum)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,14 @@ import com.bbit.ticket.dao.piaotong.HistoryDao
|
||||
import com.bbit.ticket.dao.piaotong.RedInvoiceDao
|
||||
import com.bbit.ticket.entity.request.QuickRedInvoiceRequest
|
||||
import com.bbit.ticket.entity.request.RedCreateRequest
|
||||
import com.bbit.ticket.entity.response.InvoiceDownloadUrlResponse
|
||||
import com.bbit.ticket.entity.response.QuickRedInvoiceResponse
|
||||
import com.bbit.ticket.entity.response.RedInvoiceInfoResponse
|
||||
import com.bbit.ticket.utils.CurrentUser
|
||||
import com.bbit.ticket.utils.plugins.dbQuery
|
||||
import com.bbit.ticket.utils.net.PTClient
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.bodyAsBytes
|
||||
import kotlin.uuid.Uuid
|
||||
|
||||
/**
|
||||
@@ -59,4 +62,13 @@ object PTRedService {
|
||||
suspend fun getRedInvoiceInfo(userId: Uuid, invoiceReqSerialNo: String): RedInvoiceInfoResponse? =
|
||||
dbQuery { RedInvoiceDao.findRedInfoBySerialNo(userId, invoiceReqSerialNo) }
|
||||
|
||||
suspend fun getRedInvoiceDownloadUrl(userId: Uuid, invoiceReqSerialNo: String): InvoiceDownloadUrlResponse? =
|
||||
dbQuery { RedInvoiceDao.invoiceDownloadUrl(userId, invoiceReqSerialNo) }
|
||||
|
||||
suspend fun getRedInvoicePreview(userId: Uuid, invoiceReqSerialNo: String): ByteArray? {
|
||||
val downloadUrl = getRedInvoiceDownloadUrl(userId, invoiceReqSerialNo)?.downloadUrl
|
||||
?.takeIf { it.isNotBlank() }
|
||||
?: return null
|
||||
return PTClient.client.get(downloadUrl).bodyAsBytes()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.bbit.ticket.utils
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.Base64
|
||||
|
||||
object Base64TextUtil {
|
||||
fun decodeToText(value: String?): String? {
|
||||
val text = value?.trim() ?: return null
|
||||
if (text.isEmpty()) return text
|
||||
|
||||
return runCatching {
|
||||
String(Base64.getDecoder().decode(text), StandardCharsets.UTF_8)
|
||||
}.getOrElse { text }
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package com.bbit.ticket.utils.bootstrap
|
||||
|
||||
object Global {
|
||||
|
||||
val isDev = false
|
||||
val isDev = true
|
||||
|
||||
// 请求基础地址
|
||||
var baseUrl: String
|
||||
|
||||
Reference in New Issue
Block a user