跑通PT
This commit is contained in:
@@ -4,6 +4,7 @@ import com.bbit.ticket.bootstrap.DatabaseInitializer
|
|||||||
import com.bbit.ticket.bootstrap.SeedData
|
import com.bbit.ticket.bootstrap.SeedData
|
||||||
import com.bbit.ticket.entity.common.ok
|
import com.bbit.ticket.entity.common.ok
|
||||||
import com.bbit.ticket.bootstrap.AppConfig
|
import com.bbit.ticket.bootstrap.AppConfig
|
||||||
|
import com.bbit.ticket.bootstrap.Global
|
||||||
import com.bbit.ticket.route.system.registerAuthRoutes
|
import com.bbit.ticket.route.system.registerAuthRoutes
|
||||||
import com.bbit.ticket.plugins.configureCors
|
import com.bbit.ticket.plugins.configureCors
|
||||||
import com.bbit.ticket.plugins.configureDatabase
|
import com.bbit.ticket.plugins.configureDatabase
|
||||||
@@ -14,6 +15,7 @@ import com.bbit.ticket.plugins.configureSecurity
|
|||||||
import com.bbit.ticket.plugins.configureSerialization
|
import com.bbit.ticket.plugins.configureSerialization
|
||||||
import com.bbit.ticket.plugins.configureStatusPages
|
import com.bbit.ticket.plugins.configureStatusPages
|
||||||
import com.bbit.ticket.plugins.configureTrace
|
import com.bbit.ticket.plugins.configureTrace
|
||||||
|
import com.bbit.ticket.route.piaotong.registerPTTestRoutes
|
||||||
import com.bbit.ticket.route.system.registerDictRoutes
|
import com.bbit.ticket.route.system.registerDictRoutes
|
||||||
import com.bbit.ticket.route.system.registerLogsQueryRoutes
|
import com.bbit.ticket.route.system.registerLogsQueryRoutes
|
||||||
import com.bbit.ticket.route.system.registerMenuRoutes
|
import com.bbit.ticket.route.system.registerMenuRoutes
|
||||||
@@ -25,6 +27,7 @@ import io.ktor.server.application.Application
|
|||||||
import io.ktor.server.netty.EngineMain
|
import io.ktor.server.netty.EngineMain
|
||||||
import io.ktor.server.response.respond
|
import io.ktor.server.response.respond
|
||||||
import io.ktor.server.routing.get
|
import io.ktor.server.routing.get
|
||||||
|
import io.ktor.server.routing.route
|
||||||
import io.ktor.server.routing.routing
|
import io.ktor.server.routing.routing
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
@@ -52,6 +55,7 @@ fun Application.module() {
|
|||||||
get("/health") {
|
get("/health") {
|
||||||
call.respond(ok(mapOf("status" to "UP", "service" to AppConfig.app.name)))
|
call.respond(ok(mapOf("status" to "UP", "service" to AppConfig.app.name)))
|
||||||
}
|
}
|
||||||
|
route("/api"){
|
||||||
registerAuthRoutes()
|
registerAuthRoutes()
|
||||||
registerUserRoutes()
|
registerUserRoutes()
|
||||||
registerOrgRoutes()
|
registerOrgRoutes()
|
||||||
@@ -59,5 +63,8 @@ fun Application.module() {
|
|||||||
registerMenuRoutes()
|
registerMenuRoutes()
|
||||||
registerDictRoutes()
|
registerDictRoutes()
|
||||||
registerLogsQueryRoutes()
|
registerLogsQueryRoutes()
|
||||||
|
|
||||||
|
registerPTTestRoutes()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package com.bbit.ticket.bootstrap
|
||||||
|
|
||||||
|
object Global {
|
||||||
|
|
||||||
|
// 测试账号 销售方税号
|
||||||
|
const val testTaxpayerNum = "500102201007206608"
|
||||||
|
const val testAccount = "DEMOadmin"
|
||||||
|
|
||||||
|
val isDev = true
|
||||||
|
|
||||||
|
// 请求基础地址
|
||||||
|
var baseUrl: String
|
||||||
|
|
||||||
|
// 票通私钥
|
||||||
|
var ptPrivateKey: String
|
||||||
|
|
||||||
|
// 票通公钥
|
||||||
|
var ptPublicKey: String
|
||||||
|
|
||||||
|
// 票通密码
|
||||||
|
var ptPassword: String
|
||||||
|
|
||||||
|
// 票通平台简称
|
||||||
|
var ptPlatformAlias: String
|
||||||
|
|
||||||
|
// 票通编码
|
||||||
|
var ptPlatformCode: String
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (isDev) {
|
||||||
|
baseUrl = "http://fpkj.testnw.vpiaotong.cn/tp/openapi/"
|
||||||
|
ptPrivateKey =
|
||||||
|
"MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIVLAoolDaE7m5oMB1ZrILHkMXMF6qmC8I/FCejz4hwBcj59H3rbtcycBEmExOJTGwexFkNgRakhqM+3uP3VybWu1GBYNmqVzggWKKzThul9VPE3+OTMlxeG4H63RsCO1//J0MoUavXMMkL3txkZBO5EtTqek182eePOV8fC3ZxpAgMBAAECgYBp4Gg3BTGrZaa2mWFmspd41lK1E/kPBrRA7vltMfPj3P47RrYvp7/js/Xv0+d0AyFQXcjaYelTbCokPMJT1nJumb2A/Cqy3yGKX3Z6QibvByBlCKK29lZkw8WVRGFIzCIXhGKdqukXf8RyqfhInqHpZ9AoY2W60bbSP6EXj/rhNQJBAL76SmpQOrnCI8Xu75di0eXBN/bE9tKsf7AgMkpFRhaU8VLbvd27U9vRWqtu67RY3sOeRMh38JZBwAIS8tp5hgcCQQCyrOS6vfXIUxKoWyvGyMyhqoLsiAdnxBKHh8tMINo0ioCbU+jc2dgPDipL0ym5nhvg5fCXZC2rvkKUltLEqq4PAkAqBf9b932EpKCkjFgyUq9nRCYhaeP6JbUPN3Z5e1bZ3zpfBjV4ViE0zJOMB6NcEvYpy2jNR/8rwRoUGsFPq8//AkAklw18RJyJuqFugsUzPznQvad0IuNJV7jnsmJqo6ur6NUvef6NA7ugUalNv9+imINjChO8HRLRQfRGk6B0D/P3AkBt54UBMtFefOLXgUdilwLdCUSw4KpbuBPw+cyWlMjcXCkj4rHoeksekyBH1GrBJkLqDMRqtVQUubuFwSzBAtlc"
|
||||||
|
ptPublicKey =
|
||||||
|
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJkx3HelhEm/U7jOCor29oHsIjCMSTyKbX5rpoAY8KDIs9mmr5Y9r+jvNJH8pK3u5gNnvleT6rQgJQW1mk0zHuPO00vy62tSA53fkSjtM+n0oC1Fkm4DRFd5qJgoP7uFQHR5OEffMjy2qIuxChY4Au0kq+6RruEgIttb7wUxy8TwIDAQAB"
|
||||||
|
|
||||||
|
ptPassword = "lsBnINDxtct8HZB7KCMyhWSJ"
|
||||||
|
ptPlatformAlias = "DEMK"
|
||||||
|
ptPlatformCode = "11111111"
|
||||||
|
} else {
|
||||||
|
baseUrl = "https://fpkj.vpiaotong.com/tp/openapi/"
|
||||||
|
ptPrivateKey = ""
|
||||||
|
ptPublicKey = ""
|
||||||
|
ptPassword = ""
|
||||||
|
ptPlatformAlias = ""
|
||||||
|
ptPlatformCode = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -114,10 +114,11 @@ object SeedData {
|
|||||||
it[SysUserTable.orgId] = orgId
|
it[SysUserTable.orgId] = orgId
|
||||||
it[status] = "ENABLED"
|
it[status] = "ENABLED"
|
||||||
it[updatedAt] = now
|
it[updatedAt] = now
|
||||||
|
it[taxpayerNum] = "500102201007206608"
|
||||||
|
it[account] = "DEMOadmin"
|
||||||
}
|
}
|
||||||
return@dbQuery id
|
return@dbQuery id
|
||||||
}
|
} else {
|
||||||
|
|
||||||
val inserted = SysUserTable.insert {
|
val inserted = SysUserTable.insert {
|
||||||
it[username] = ADMIN_USERNAME
|
it[username] = ADMIN_USERNAME
|
||||||
it[passwordHash] = PasswordService.hash(ADMIN_INIT_PASSWORD)
|
it[passwordHash] = PasswordService.hash(ADMIN_INIT_PASSWORD)
|
||||||
@@ -126,10 +127,12 @@ object SeedData {
|
|||||||
it[SysUserTable.orgId] = orgId
|
it[SysUserTable.orgId] = orgId
|
||||||
it[status] = "ENABLED"
|
it[status] = "ENABLED"
|
||||||
it[tokenVersion] = 1
|
it[tokenVersion] = 1
|
||||||
it[createdAt] = now
|
it[taxpayerNum] = "500102201007206608"
|
||||||
|
it[account] = "DEMOadmin"
|
||||||
}
|
}
|
||||||
inserted[SysUserTable.id]
|
inserted[SysUserTable.id]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun upsertUserRole(userId: Uuid, roleId: Uuid) = dbQuery {
|
private suspend fun upsertUserRole(userId: Uuid, roleId: Uuid) = dbQuery {
|
||||||
val exists = SysUserRoleTable.selectAll()
|
val exists = SysUserRoleTable.selectAll()
|
||||||
@@ -145,32 +148,357 @@ object SeedData {
|
|||||||
|
|
||||||
private suspend fun upsertMenus(now: OffsetDateTime): List<Uuid> {
|
private suspend fun upsertMenus(now: OffsetDateTime): List<Uuid> {
|
||||||
val seedMenus = listOf(
|
val seedMenus = listOf(
|
||||||
SeedMenu("dashboard", null, "MENU", "工作台", "Dashboard", "/dashboard", "dashboard/index", "LayoutDashboard", null, 10, true, true),
|
SeedMenu(
|
||||||
SeedMenu("system", null, "CATALOG", "系统管理", "SystemRoot", "/system", null, "Settings", null, 20, true, false),
|
"dashboard",
|
||||||
SeedMenu("system_user", "system", "MENU", "用户管理", "SystemUsers", "/system/users", "system/users/index", "Users", "system:user:view", 10, true, true),
|
null,
|
||||||
SeedMenu("system_user_create", "system_user", "BUTTON", "新增用户", "SystemUserCreate", null, null, null, "system:user:create", 1, true, false),
|
"MENU",
|
||||||
SeedMenu("system_user_update", "system_user", "BUTTON", "修改用户", "SystemUserUpdate", null, null, null, "system:user:update", 2, true, false),
|
"工作台",
|
||||||
SeedMenu("system_user_delete", "system_user", "BUTTON", "删除用户", "SystemUserDelete", null, null, null, "system:user:delete", 3, true, false),
|
"Dashboard",
|
||||||
SeedMenu("system_org", "system", "MENU", "组织管理", "SystemOrgs", "/system/orgs", "system/orgs/index", "Building2", "system:org:view", 20, true, true),
|
"/dashboard",
|
||||||
SeedMenu("system_org_create", "system_org", "BUTTON", "新增组织", "SystemOrgCreate", null, null, null, "system:org:create", 1, true, false),
|
"dashboard/index",
|
||||||
SeedMenu("system_org_update", "system_org", "BUTTON", "更新组织", "SystemOrgUpdate", null, null, null, "system:org:update", 2, true, false),
|
"LayoutDashboard",
|
||||||
SeedMenu("system_org_delete", "system_org", "BUTTON", "删除组织", "SystemOrgDelete", null, null, null, "system:org:delete", 3, true, false),
|
null,
|
||||||
SeedMenu("system_role", "system", "MENU", "角色管理", "SystemRoles", "/system/roles", "system/roles/index", "Shield", "system:role:view", 30, true, true),
|
10,
|
||||||
SeedMenu("system_role_create", "system_role", "BUTTON", "新增角色", "SystemRoleCreate", null, null, null, "system:role:create", 1, true, false),
|
true,
|
||||||
SeedMenu("system_role_update", "system_role", "BUTTON", "更新角色", "SystemRoleUpdate", null, null, null, "system:role:update", 2, true, false),
|
true
|
||||||
SeedMenu("system_role_delete", "system_role", "BUTTON", "删除角色", "SystemRoleDelete", null, null, null, "system:role:delete", 3, true, false),
|
),
|
||||||
SeedMenu("system_role_assign", "system_role", "BUTTON", "分配角色权限", "SystemRoleAssign", null, null, null, "system:role:assign", 4, true, false),
|
SeedMenu(
|
||||||
SeedMenu("system_menu", "system", "MENU", "菜单管理", "SystemMenus", "/system/menus", "system/menus/index", "PanelLeft", "system:menu:view", 40, true, true),
|
"system",
|
||||||
SeedMenu("system_menu_create", "system_menu", "BUTTON", "新增菜单", "SystemMenuCreate", null, null, null, "system:menu:create", 1, true, false),
|
null,
|
||||||
SeedMenu("system_menu_update", "system_menu", "BUTTON", "更新菜单", "SystemMenuUpdate", null, null, null, "system:menu:update", 2, true, false),
|
"CATALOG",
|
||||||
SeedMenu("system_menu_delete", "system_menu", "BUTTON", "删除菜单", "SystemMenuDelete", null, null, null, "system:menu:delete", 3, true, false),
|
"系统管理",
|
||||||
SeedMenu("system_dict", "system", "MENU", "字典管理", "SystemDict", "/system/dicts", "system/dicts/index", "BookType", "system:dict:view", 50, true, true),
|
"SystemRoot",
|
||||||
SeedMenu("system_dict_create", "system_dict", "BUTTON", "新增字典", "SystemDictCreate", null, null, null, "system:dict:create", 1, true, false),
|
"/system",
|
||||||
SeedMenu("system_dict_update", "system_dict", "BUTTON", "更新字典", "SystemDictUpdate", null, null, null, "system:dict:update", 2, true, false),
|
null,
|
||||||
SeedMenu("system_dict_delete", "system_dict", "BUTTON", "删除字典", "SystemDictDelete", null, null, null, "system:dict:delete", 3, true, false),
|
"Settings",
|
||||||
|
null,
|
||||||
|
20,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_user",
|
||||||
|
"system",
|
||||||
|
"MENU",
|
||||||
|
"用户管理",
|
||||||
|
"SystemUsers",
|
||||||
|
"/system/users",
|
||||||
|
"system/users/index",
|
||||||
|
"Users",
|
||||||
|
"system:user:view",
|
||||||
|
10,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_user_create",
|
||||||
|
"system_user",
|
||||||
|
"BUTTON",
|
||||||
|
"新增用户",
|
||||||
|
"SystemUserCreate",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"system:user:create",
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_user_update",
|
||||||
|
"system_user",
|
||||||
|
"BUTTON",
|
||||||
|
"修改用户",
|
||||||
|
"SystemUserUpdate",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"system:user:update",
|
||||||
|
2,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_user_delete",
|
||||||
|
"system_user",
|
||||||
|
"BUTTON",
|
||||||
|
"删除用户",
|
||||||
|
"SystemUserDelete",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"system:user:delete",
|
||||||
|
3,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_org",
|
||||||
|
"system",
|
||||||
|
"MENU",
|
||||||
|
"组织管理",
|
||||||
|
"SystemOrgs",
|
||||||
|
"/system/orgs",
|
||||||
|
"system/orgs/index",
|
||||||
|
"Building2",
|
||||||
|
"system:org:view",
|
||||||
|
20,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_org_create",
|
||||||
|
"system_org",
|
||||||
|
"BUTTON",
|
||||||
|
"新增组织",
|
||||||
|
"SystemOrgCreate",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"system:org:create",
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_org_update",
|
||||||
|
"system_org",
|
||||||
|
"BUTTON",
|
||||||
|
"更新组织",
|
||||||
|
"SystemOrgUpdate",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"system:org:update",
|
||||||
|
2,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_org_delete",
|
||||||
|
"system_org",
|
||||||
|
"BUTTON",
|
||||||
|
"删除组织",
|
||||||
|
"SystemOrgDelete",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"system:org:delete",
|
||||||
|
3,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_role",
|
||||||
|
"system",
|
||||||
|
"MENU",
|
||||||
|
"角色管理",
|
||||||
|
"SystemRoles",
|
||||||
|
"/system/roles",
|
||||||
|
"system/roles/index",
|
||||||
|
"Shield",
|
||||||
|
"system:role:view",
|
||||||
|
30,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_role_create",
|
||||||
|
"system_role",
|
||||||
|
"BUTTON",
|
||||||
|
"新增角色",
|
||||||
|
"SystemRoleCreate",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"system:role:create",
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_role_update",
|
||||||
|
"system_role",
|
||||||
|
"BUTTON",
|
||||||
|
"更新角色",
|
||||||
|
"SystemRoleUpdate",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"system:role:update",
|
||||||
|
2,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_role_delete",
|
||||||
|
"system_role",
|
||||||
|
"BUTTON",
|
||||||
|
"删除角色",
|
||||||
|
"SystemRoleDelete",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"system:role:delete",
|
||||||
|
3,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_role_assign",
|
||||||
|
"system_role",
|
||||||
|
"BUTTON",
|
||||||
|
"分配角色权限",
|
||||||
|
"SystemRoleAssign",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"system:role:assign",
|
||||||
|
4,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_menu",
|
||||||
|
"system",
|
||||||
|
"MENU",
|
||||||
|
"菜单管理",
|
||||||
|
"SystemMenus",
|
||||||
|
"/system/menus",
|
||||||
|
"system/menus/index",
|
||||||
|
"PanelLeft",
|
||||||
|
"system:menu:view",
|
||||||
|
40,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_menu_create",
|
||||||
|
"system_menu",
|
||||||
|
"BUTTON",
|
||||||
|
"新增菜单",
|
||||||
|
"SystemMenuCreate",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"system:menu:create",
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_menu_update",
|
||||||
|
"system_menu",
|
||||||
|
"BUTTON",
|
||||||
|
"更新菜单",
|
||||||
|
"SystemMenuUpdate",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"system:menu:update",
|
||||||
|
2,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_menu_delete",
|
||||||
|
"system_menu",
|
||||||
|
"BUTTON",
|
||||||
|
"删除菜单",
|
||||||
|
"SystemMenuDelete",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"system:menu:delete",
|
||||||
|
3,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_dict",
|
||||||
|
"system",
|
||||||
|
"MENU",
|
||||||
|
"字典管理",
|
||||||
|
"SystemDict",
|
||||||
|
"/system/dicts",
|
||||||
|
"system/dicts/index",
|
||||||
|
"BookType",
|
||||||
|
"system:dict:view",
|
||||||
|
50,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_dict_create",
|
||||||
|
"system_dict",
|
||||||
|
"BUTTON",
|
||||||
|
"新增字典",
|
||||||
|
"SystemDictCreate",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"system:dict:create",
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_dict_update",
|
||||||
|
"system_dict",
|
||||||
|
"BUTTON",
|
||||||
|
"更新字典",
|
||||||
|
"SystemDictUpdate",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"system:dict:update",
|
||||||
|
2,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"system_dict_delete",
|
||||||
|
"system_dict",
|
||||||
|
"BUTTON",
|
||||||
|
"删除字典",
|
||||||
|
"SystemDictDelete",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
"system:dict:delete",
|
||||||
|
3,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
),
|
||||||
SeedMenu("logs", null, "CATALOG", "日志管理", "LogsRoot", "/logs", null, "Logs", null, 30, true, false),
|
SeedMenu("logs", null, "CATALOG", "日志管理", "LogsRoot", "/logs", null, "Logs", null, 30, true, false),
|
||||||
SeedMenu("logs_operation", "logs", "MENU", "操作日志", "LogsOperation", "/logs/operation", "logs/operation/index", "ScrollText", "log:operation:view", 10, true, true),
|
SeedMenu(
|
||||||
SeedMenu("logs_api_access", "logs", "MENU", "接口日志", "LogsApiAccess", "/logs/api-access", "logs/api-access/index", "Waypoints", "log:api-access:view", 20, true, true),
|
"logs_operation",
|
||||||
|
"logs",
|
||||||
|
"MENU",
|
||||||
|
"操作日志",
|
||||||
|
"LogsOperation",
|
||||||
|
"/logs/operation",
|
||||||
|
"logs/operation/index",
|
||||||
|
"ScrollText",
|
||||||
|
"log:operation:view",
|
||||||
|
10,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
SeedMenu(
|
||||||
|
"logs_api_access",
|
||||||
|
"logs",
|
||||||
|
"MENU",
|
||||||
|
"接口日志",
|
||||||
|
"LogsApiAccess",
|
||||||
|
"/logs/api-access",
|
||||||
|
"logs/api-access/index",
|
||||||
|
"Waypoints",
|
||||||
|
"log:api-access:view",
|
||||||
|
20,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
val idMap = mutableMapOf<String, Uuid>()
|
val idMap = mutableMapOf<String, Uuid>()
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import org.jetbrains.exposed.v1.core.eq
|
|||||||
import org.jetbrains.exposed.v1.core.inList
|
import org.jetbrains.exposed.v1.core.inList
|
||||||
import org.jetbrains.exposed.v1.core.isNull
|
import org.jetbrains.exposed.v1.core.isNull
|
||||||
import org.jetbrains.exposed.v1.core.like
|
import org.jetbrains.exposed.v1.core.like
|
||||||
import org.jetbrains.exposed.v1.jdbc.Query
|
|
||||||
import org.jetbrains.exposed.v1.jdbc.deleteWhere
|
import org.jetbrains.exposed.v1.jdbc.deleteWhere
|
||||||
import org.jetbrains.exposed.v1.jdbc.insert
|
import org.jetbrains.exposed.v1.jdbc.insert
|
||||||
import org.jetbrains.exposed.v1.jdbc.insertIgnore
|
import org.jetbrains.exposed.v1.jdbc.insertIgnore
|
||||||
@@ -59,10 +58,10 @@ object UserDao {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun findByUsername(username: String): ResultRow? =
|
fun findByUsername(username: String): ResultRow? =
|
||||||
activeUsers().where { SysUserTable.username eq username }.singleOrNull()
|
SysUserTable.selectAll().where { activeWhere() and (SysUserTable.username eq username) }.singleOrNull()
|
||||||
|
|
||||||
fun requireActive(id: Uuid): ResultRow =
|
fun requireActive(id: Uuid): ResultRow =
|
||||||
activeUsers().where { SysUserTable.id eq id }.singleOrNull()
|
SysUserTable.selectAll().where { activeWhere() and (SysUserTable.id eq id) }.singleOrNull()
|
||||||
?: throw BizException(
|
?: throw BizException(
|
||||||
ErrorCode.USER_NOT_FOUND.code,
|
ErrorCode.USER_NOT_FOUND.code,
|
||||||
ErrorCode.USER_NOT_FOUND.message,
|
ErrorCode.USER_NOT_FOUND.message,
|
||||||
@@ -179,11 +178,10 @@ object UserDao {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun activeUsers(): Query =
|
private fun activeWhere(): Op<Boolean> = SysUserTable.deletedAt.isNull()
|
||||||
SysUserTable.selectAll().where { SysUserTable.deletedAt.isNull() }
|
|
||||||
|
|
||||||
private fun buildWhere(username: String?, nickname: String?, status: String?, orgId: Uuid?): Op<Boolean> {
|
private fun buildWhere(username: String?, nickname: String?, status: String?, orgId: Uuid?): Op<Boolean> {
|
||||||
var where: Op<Boolean> = SysUserTable.deletedAt.isNull()
|
var where: Op<Boolean> = activeWhere()
|
||||||
if (!username.isNullOrBlank()) {
|
if (!username.isNullOrBlank()) {
|
||||||
where = where and (SysUserTable.username like "%$username%")
|
where = where and (SysUserTable.username like "%$username%")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,5 +28,8 @@ object SysUserTable : Table("sys_user") {
|
|||||||
val deletedBy = uuid("deleted_by").nullable()
|
val deletedBy = uuid("deleted_by").nullable()
|
||||||
val version = integer("version").default(1)
|
val version = integer("version").default(1)
|
||||||
|
|
||||||
|
val taxpayerNum = varchar("taxpayer_num", 50).nullable()
|
||||||
|
val account = varchar("account", 50).nullable()
|
||||||
|
|
||||||
override val primaryKey = PrimaryKey(id)
|
override val primaryKey = PrimaryKey(id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.bbit.ticket.entity.common
|
||||||
|
|
||||||
|
class PTException(
|
||||||
|
val code: String,
|
||||||
|
override val message: String,
|
||||||
|
val serialNo: String? = null
|
||||||
|
) : RuntimeException(message)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.bbit.ticket.entity.request
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class TaxBureauAuthReq(
|
||||||
|
val taxpayerNum: String,
|
||||||
|
val account: String
|
||||||
|
)
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.bbit.ticket.entity.response
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class PTResponse<T>(
|
||||||
|
val code: String,
|
||||||
|
val msg: String,
|
||||||
|
val sign: String,
|
||||||
|
val serialNo: String,
|
||||||
|
val content: T? = null
|
||||||
|
)
|
||||||
|
@Serializable
|
||||||
|
data class PTResponseString(
|
||||||
|
val code: String,
|
||||||
|
val msg: String,
|
||||||
|
val sign: String,
|
||||||
|
val serialNo: String,
|
||||||
|
val content: String? = null
|
||||||
|
)
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package com.bbit.ticket.entity.response
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class TaxBureauAccountAuthContent(
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信用户绑定状态
|
||||||
|
* 0-未绑定
|
||||||
|
* 1-已绑定
|
||||||
|
*/
|
||||||
|
val wechatUserBindStatus: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作建议
|
||||||
|
*/
|
||||||
|
val operationProposed: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实名认证状态
|
||||||
|
*/
|
||||||
|
val authStatus: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录认证状态
|
||||||
|
*/
|
||||||
|
val loginAuthStatus: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最后登录认证时间
|
||||||
|
*/
|
||||||
|
val lastLoginAuthTime: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最后风险认证时间
|
||||||
|
*/
|
||||||
|
val lastRiskAuthTime: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最后认证成功时间
|
||||||
|
*/
|
||||||
|
val lastAuthSuccTime: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 身份类型
|
||||||
|
* 01-法人
|
||||||
|
*/
|
||||||
|
val identityType: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 姓名
|
||||||
|
*/
|
||||||
|
val name: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 纳税人识别号
|
||||||
|
*/
|
||||||
|
val taxpayerNum: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许切换
|
||||||
|
*/
|
||||||
|
val switchable: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 风险认证状态
|
||||||
|
*/
|
||||||
|
val riskAuthStatus: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 电子税局账号
|
||||||
|
*/
|
||||||
|
val account: String
|
||||||
|
)
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.bbit.ticket.route.piaotong
|
||||||
|
|
||||||
|
import com.bbit.ticket.bootstrap.Global
|
||||||
|
import com.bbit.ticket.entity.common.PTException
|
||||||
|
import com.bbit.ticket.entity.common.fail
|
||||||
|
import com.bbit.ticket.entity.common.ok
|
||||||
|
import com.bbit.ticket.entity.request.TaxBureauAuthReq
|
||||||
|
import com.bbit.ticket.entity.response.TaxBureauAccountAuthContent
|
||||||
|
import com.bbit.ticket.service.piaotong.PTAuthService
|
||||||
|
import com.bbit.ticket.utils.requireCurrentUser
|
||||||
|
import io.ktor.server.auth.authenticate
|
||||||
|
import io.ktor.server.response.respond
|
||||||
|
import io.ktor.server.routing.Route
|
||||||
|
import io.ktor.server.routing.get
|
||||||
|
import io.ktor.server.routing.route
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.decodeFromJsonElement
|
||||||
|
|
||||||
|
fun Route.registerPTTestRoutes() {
|
||||||
|
route("/pt") {
|
||||||
|
authenticate("auth-jwt") {
|
||||||
|
get("/info") {
|
||||||
|
try {
|
||||||
|
val currentUser = call.requireCurrentUser()
|
||||||
|
val taxpayerNum = currentUser.taxpayerNum ?: Global.testTaxpayerNum
|
||||||
|
val account = currentUser.account ?: Global.testAccount
|
||||||
|
val response = PTAuthService.getTaxBureauAccountAuthStatus(
|
||||||
|
TaxBureauAuthReq(taxpayerNum, account)
|
||||||
|
)
|
||||||
|
call.respond(ok(response))
|
||||||
|
} catch (e: PTException) {
|
||||||
|
call.respond(
|
||||||
|
fail(
|
||||||
|
code = e.code,
|
||||||
|
message = e.message,
|
||||||
|
traceId = e.serialNo
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ import kotlin.time.TimeSource
|
|||||||
|
|
||||||
|
|
||||||
fun Route.registerAuthRoutes() {
|
fun Route.registerAuthRoutes() {
|
||||||
route("/api/auth") {
|
route("/auth") {
|
||||||
post("/login") {
|
post("/login") {
|
||||||
val start = TimeSource.Monotonic.markNow()
|
val start = TimeSource.Monotonic.markNow()
|
||||||
val request = call.receive<LoginRequest>()
|
val request = call.receive<LoginRequest>()
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import kotlin.uuid.ExperimentalUuidApi
|
|||||||
|
|
||||||
fun Route.registerDictRoutes() {
|
fun Route.registerDictRoutes() {
|
||||||
authenticate("auth-jwt") {
|
authenticate("auth-jwt") {
|
||||||
route("/api/system/dict-types") {
|
route("/system/dict-types") {
|
||||||
get {
|
get {
|
||||||
call.requirePermission("system:dict:view")
|
call.requirePermission("system:dict:view")
|
||||||
val page = call.queryInt("page", 1)
|
val page = call.queryInt("page", 1)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import io.ktor.server.routing.route
|
|||||||
|
|
||||||
fun Route.registerLogsQueryRoutes() {
|
fun Route.registerLogsQueryRoutes() {
|
||||||
authenticate("auth-jwt") {
|
authenticate("auth-jwt") {
|
||||||
route("/api/logs") {
|
route("/logs") {
|
||||||
get("/operation") {
|
get("/operation") {
|
||||||
call.requirePermission("log:operation:view")
|
call.requirePermission("log:operation:view")
|
||||||
val page = call.queryInt("page", 1)
|
val page = call.queryInt("page", 1)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import kotlin.uuid.ExperimentalUuidApi
|
|||||||
|
|
||||||
fun Route.registerMenuRoutes() {
|
fun Route.registerMenuRoutes() {
|
||||||
authenticate("auth-jwt") {
|
authenticate("auth-jwt") {
|
||||||
route("/api/system/menus") {
|
route("/system/menus") {
|
||||||
get {
|
get {
|
||||||
call.requirePermission("system:menu:view")
|
call.requirePermission("system:menu:view")
|
||||||
call.respond(ok(MenuService.tree()))
|
call.respond(ok(MenuService.tree()))
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import kotlin.uuid.ExperimentalUuidApi
|
|||||||
|
|
||||||
fun Route.registerOrgRoutes() {
|
fun Route.registerOrgRoutes() {
|
||||||
authenticate("auth-jwt") {
|
authenticate("auth-jwt") {
|
||||||
route("/api/system/orgs") {
|
route("/system/orgs") {
|
||||||
get {
|
get {
|
||||||
call.requirePermission("system:org:view")
|
call.requirePermission("system:org:view")
|
||||||
call.respond(ok(OrgService.tree()))
|
call.respond(ok(OrgService.tree()))
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import kotlin.uuid.ExperimentalUuidApi
|
|||||||
|
|
||||||
fun Route.registerRoleRoutes() {
|
fun Route.registerRoleRoutes() {
|
||||||
authenticate("auth-jwt") {
|
authenticate("auth-jwt") {
|
||||||
route("/api/system/roles") {
|
route("/system/roles") {
|
||||||
get {
|
get {
|
||||||
call.requirePermission("system:role:view")
|
call.requirePermission("system:role:view")
|
||||||
val page = call.queryInt("page", 1)
|
val page = call.queryInt("page", 1)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import kotlin.uuid.ExperimentalUuidApi
|
|||||||
|
|
||||||
fun Route.registerUserRoutes() {
|
fun Route.registerUserRoutes() {
|
||||||
authenticate("auth-jwt") {
|
authenticate("auth-jwt") {
|
||||||
route("/api/system/users") {
|
route("/system/users") {
|
||||||
get {
|
get {
|
||||||
call.requirePermission("system:user:view")
|
call.requirePermission("system:user:view")
|
||||||
val page = call.queryInt("page", 1)
|
val page = call.queryInt("page", 1)
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.bbit.ticket.service.piaotong
|
||||||
|
|
||||||
|
import com.bbit.ticket.entity.request.TaxBureauAuthReq
|
||||||
|
import com.bbit.ticket.entity.response.TaxBureauAccountAuthContent
|
||||||
|
import com.bbit.ticket.utils.net.PTClient
|
||||||
|
|
||||||
|
object PTAuthService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询数电账号认证状态
|
||||||
|
* 此接口用来查询数电账号的认证状态,会返回当
|
||||||
|
*
|
||||||
|
* @param taxpayerNum 纳税人识别号
|
||||||
|
* @param account 账号
|
||||||
|
*/
|
||||||
|
suspend fun getTaxBureauAccountAuthStatus(req : TaxBureauAuthReq): String {
|
||||||
|
val res = PTClient.ptPost<TaxBureauAuthReq, TaxBureauAccountAuthContent>(
|
||||||
|
url = "getTaxBureauAccountAuthStatus.pt",
|
||||||
|
body = req
|
||||||
|
)
|
||||||
|
println("res = $res")
|
||||||
|
return res.taxpayerNum
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import com.bbit.ticket.database.system.SysRoleMenuTable
|
|||||||
import com.bbit.ticket.database.system.SysRoleTable
|
import com.bbit.ticket.database.system.SysRoleTable
|
||||||
import com.bbit.ticket.database.system.SysUserRoleTable
|
import com.bbit.ticket.database.system.SysUserRoleTable
|
||||||
import com.bbit.ticket.database.system.SysUserTable
|
import com.bbit.ticket.database.system.SysUserTable
|
||||||
|
import com.bbit.ticket.database.system.SysUserTable.account
|
||||||
import com.bbit.ticket.plugins.dbQuery
|
import com.bbit.ticket.plugins.dbQuery
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.server.application.ApplicationCall
|
import io.ktor.server.application.ApplicationCall
|
||||||
@@ -31,6 +32,8 @@ data class CurrentUser(
|
|||||||
val tokenVersion: Int,
|
val tokenVersion: Int,
|
||||||
val roleCodes: Set<String>,
|
val roleCodes: Set<String>,
|
||||||
val permissions: Set<String>,
|
val permissions: Set<String>,
|
||||||
|
val taxpayerNum: String?,
|
||||||
|
val account: String?,
|
||||||
) {
|
) {
|
||||||
val isSuperAdmin: Boolean
|
val isSuperAdmin: Boolean
|
||||||
get() = roleCodes.contains("SUPER_ADMIN")
|
get() = roleCodes.contains("SUPER_ADMIN")
|
||||||
@@ -135,6 +138,8 @@ suspend fun ApplicationCall.requireCurrentUser(): CurrentUser {
|
|||||||
tokenVersion = userRow[SysUserTable.tokenVersion],
|
tokenVersion = userRow[SysUserTable.tokenVersion],
|
||||||
roleCodes = roleCodes,
|
roleCodes = roleCodes,
|
||||||
permissions = permissions,
|
permissions = permissions,
|
||||||
|
taxpayerNum = userRow[SysUserTable.taxpayerNum],
|
||||||
|
account = userRow[SysUserTable.account],
|
||||||
)
|
)
|
||||||
|
|
||||||
attributes.put(CurrentUserKey, currentUser)
|
attributes.put(CurrentUserKey, currentUser)
|
||||||
|
|||||||
@@ -0,0 +1,184 @@
|
|||||||
|
package com.bbit.ticket.utils.net
|
||||||
|
|
||||||
|
import com.bbit.ticket.bootstrap.Global
|
||||||
|
import com.bbit.ticket.entity.common.PTException
|
||||||
|
import com.bbit.ticket.entity.response.PTResponse
|
||||||
|
import io.ktor.client.*
|
||||||
|
import io.ktor.client.engine.cio.*
|
||||||
|
import io.ktor.client.plugins.contentnegotiation.*
|
||||||
|
import io.ktor.client.request.*
|
||||||
|
import io.ktor.client.statement.*
|
||||||
|
import io.ktor.http.*
|
||||||
|
import io.ktor.serialization.kotlinx.json.*
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.JsonElement
|
||||||
|
import kotlinx.serialization.json.buildJsonObject
|
||||||
|
import kotlinx.serialization.json.decodeFromJsonElement
|
||||||
|
import kotlinx.serialization.json.jsonObject
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object PTClient {
|
||||||
|
|
||||||
|
val client = HttpClient(CIO) {
|
||||||
|
|
||||||
|
install(ContentNegotiation) {
|
||||||
|
json(
|
||||||
|
Json {
|
||||||
|
ignoreUnknownKeys = true
|
||||||
|
prettyPrint = true
|
||||||
|
isLenient = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET 请求
|
||||||
|
*/
|
||||||
|
suspend inline fun <reified Req, reified Resp> ptGet(
|
||||||
|
url: String,
|
||||||
|
queryParams: Req,
|
||||||
|
headers: Map<String, String> = emptyMap()
|
||||||
|
): Resp {
|
||||||
|
|
||||||
|
val response = client.get(Global.baseUrl + url) {
|
||||||
|
|
||||||
|
url {
|
||||||
|
// ⚠️ 把 Req 转成 JSON 再拆成 query(统一协议口径)
|
||||||
|
val json = Json.encodeToString(queryParams)
|
||||||
|
val element = Json.parseToJsonElement(json).jsonObject
|
||||||
|
|
||||||
|
element.forEach { (k, v) ->
|
||||||
|
parameters.append(k, v.toString().trim('"'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
headers.forEach { (k, v) ->
|
||||||
|
header(k, v)
|
||||||
|
}
|
||||||
|
}.bodyAsText()
|
||||||
|
|
||||||
|
val decrypted = disposeResponse(response)
|
||||||
|
|
||||||
|
val result = Json.decodeFromString<PTResponse<JsonElement>>(decrypted)
|
||||||
|
|
||||||
|
if (result.code != "0000") {
|
||||||
|
throw PTException(
|
||||||
|
code = result.code,
|
||||||
|
message = result.msg,
|
||||||
|
serialNo = result.serialNo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Json.decodeFromJsonElement(result.content!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend inline fun <reified Req, reified Resp> ptPost(
|
||||||
|
url: String,
|
||||||
|
body: Req,
|
||||||
|
headers: Map<String, String> = emptyMap()
|
||||||
|
): Resp {
|
||||||
|
|
||||||
|
val response = client.post(Global.baseUrl + url) {
|
||||||
|
contentType(ContentType.Application.Json)
|
||||||
|
|
||||||
|
headers.forEach { (k, v) ->
|
||||||
|
header(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
setBody(buildRequestData(Json.encodeToString(body)))
|
||||||
|
}.bodyAsText()
|
||||||
|
|
||||||
|
val decrypted = disposeResponse(response)
|
||||||
|
|
||||||
|
val result = Json.decodeFromString<PTResponse<JsonElement>>(decrypted)
|
||||||
|
|
||||||
|
if (result.code != "0000") {
|
||||||
|
throw PTException(
|
||||||
|
code = result.code,
|
||||||
|
message = result.msg,
|
||||||
|
serialNo = result.serialNo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Json.decodeFromJsonElement(result.content!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭
|
||||||
|
*/
|
||||||
|
fun close() {
|
||||||
|
client.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
fun buildRequestData(content: String): String {
|
||||||
|
val reqContent: String = SecurityUtil.encrypt3DES(Global.ptPassword, content) ?: ""
|
||||||
|
val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
|
||||||
|
val map = HashMap<String, String>()
|
||||||
|
map["platformCode"] = Global.ptPlatformCode
|
||||||
|
map["signType"] = "RSA"
|
||||||
|
map["format"] = "JSON"
|
||||||
|
map["version"] = "1.0"
|
||||||
|
map["content"] = reqContent
|
||||||
|
map["timestamp"] = sdf.format(Date())
|
||||||
|
map["serialNo"] = ptDate(Global.ptPlatformAlias)
|
||||||
|
map["sign"] = RSAUtil.sign(RSAUtil.getSignatureContent(map), Global.ptPrivateKey) ?: ""
|
||||||
|
return Json.encodeToString(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun disposeResponse(
|
||||||
|
jsonStr: String,
|
||||||
|
): String {
|
||||||
|
|
||||||
|
val json = Json.parseToJsonElement(jsonStr).jsonObject
|
||||||
|
// 1. 转 Map(用于签名验证)
|
||||||
|
val mutableMap = json
|
||||||
|
.toMutableMap()
|
||||||
|
.mapValues { it.value.toString().trim('"') }
|
||||||
|
.toMutableMap()
|
||||||
|
|
||||||
|
// 2. 取出 sign
|
||||||
|
val sign = mutableMap.remove("sign")
|
||||||
|
?: throw IllegalStateException("sign 为空")
|
||||||
|
|
||||||
|
// 3. 验签内容拼接
|
||||||
|
val signContent = RSAUtil.getSignatureContent(mutableMap)
|
||||||
|
|
||||||
|
val verifyOk = RSAUtil.verify(signContent, sign, Global.ptPublicKey)
|
||||||
|
|
||||||
|
if (!verifyOk) {
|
||||||
|
throw IllegalStateException("验签失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 解密 content(3DES)
|
||||||
|
val encryptedContent = mutableMap["content"]
|
||||||
|
?: throw IllegalStateException("content 为空")
|
||||||
|
|
||||||
|
val plainContent = SecurityUtil.decrypt3DES(Global.ptPassword, encryptedContent) ?: ""
|
||||||
|
|
||||||
|
// 5. 替换 content
|
||||||
|
val resultJson = buildJsonObject {
|
||||||
|
json.forEach { (k, v) ->
|
||||||
|
if (k == "content") {
|
||||||
|
put("content", Json.parseToJsonElement(plainContent))
|
||||||
|
} else {
|
||||||
|
put(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultJson.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ptDate(prefix: String?): String {
|
||||||
|
val date = Date()
|
||||||
|
val sdf = SimpleDateFormat("YYYYMMddHHmmss")
|
||||||
|
val str = prefix + sdf.format(date) + (Math.random() * 90 + 10).toInt()
|
||||||
|
println(str)
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
package com.bbit.ticket.utils.net
|
||||||
|
|
||||||
|
import java.security.*
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec
|
||||||
|
import java.security.spec.X509EncodedKeySpec
|
||||||
|
import java.util.*
|
||||||
|
import javax.crypto.Cipher
|
||||||
|
|
||||||
|
object RSAUtil {
|
||||||
|
|
||||||
|
private const val SIGN_ALGORITHMS = "SHA1WithRSA"
|
||||||
|
private val DEFAULT_CHARSET = Charsets.UTF_8
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// 签名
|
||||||
|
// =========================
|
||||||
|
fun sign(content: String, privateKey: String): String? {
|
||||||
|
return try {
|
||||||
|
val keySpec = PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey))
|
||||||
|
val keyFactory = KeyFactory.getInstance("RSA")
|
||||||
|
val priKey = keyFactory.generatePrivate(keySpec)
|
||||||
|
|
||||||
|
val signature = Signature.getInstance(SIGN_ALGORITHMS)
|
||||||
|
signature.initSign(priKey)
|
||||||
|
signature.update(content.toByteArray(DEFAULT_CHARSET))
|
||||||
|
|
||||||
|
Base64.getEncoder().encodeToString(signature.sign())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// 验签
|
||||||
|
// =========================
|
||||||
|
fun verify(content: String, sign: String, publicKey: String): Boolean {
|
||||||
|
return try {
|
||||||
|
val keyFactory = KeyFactory.getInstance("RSA")
|
||||||
|
val pubKey = keyFactory.generatePublic(
|
||||||
|
X509EncodedKeySpec(Base64.getDecoder().decode(publicKey))
|
||||||
|
)
|
||||||
|
|
||||||
|
val signature = Signature.getInstance(SIGN_ALGORITHMS)
|
||||||
|
signature.initVerify(pubKey)
|
||||||
|
signature.update(content.toByteArray(DEFAULT_CHARSET))
|
||||||
|
|
||||||
|
signature.verify(Base64.getDecoder().decode(sign))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// RSA 加密
|
||||||
|
// =========================
|
||||||
|
fun encrypt(content: String, publicKeyStr: String): String {
|
||||||
|
val cipher = Cipher.getInstance("RSA")
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKeyStr))
|
||||||
|
|
||||||
|
val encrypted = cipher.doFinal(content.toByteArray(DEFAULT_CHARSET))
|
||||||
|
return Base64.getEncoder().encodeToString(encrypted)
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// RSA 解密
|
||||||
|
// =========================
|
||||||
|
fun decrypt(content: String, privateKeyStr: String): String {
|
||||||
|
val cipher = Cipher.getInstance("RSA")
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKeyStr))
|
||||||
|
|
||||||
|
val decrypted = cipher.doFinal(Base64.getDecoder().decode(content))
|
||||||
|
return String(decrypted, DEFAULT_CHARSET)
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// Key 转换
|
||||||
|
// =========================
|
||||||
|
fun getPublicKey(key: String): PublicKey {
|
||||||
|
val keyBytes = Base64.getDecoder().decode(key)
|
||||||
|
val keySpec = X509EncodedKeySpec(keyBytes)
|
||||||
|
val keyFactory = KeyFactory.getInstance("RSA")
|
||||||
|
return keyFactory.generatePublic(keySpec)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPrivateKey(key: String): PrivateKey {
|
||||||
|
val keyBytes = Base64.getDecoder().decode(key)
|
||||||
|
val keySpec = PKCS8EncodedKeySpec(keyBytes)
|
||||||
|
val keyFactory = KeyFactory.getInstance("RSA")
|
||||||
|
return keyFactory.generatePrivate(keySpec)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getKeyString(key: Key): String {
|
||||||
|
return Base64.getEncoder().encodeToString(key.encoded)
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// 参数排序拼接(签名字符串)
|
||||||
|
// =========================
|
||||||
|
fun getSignatureContent(params: Map<String, Any?>): String {
|
||||||
|
return params
|
||||||
|
.filterValues { it != null }
|
||||||
|
.toSortedMap()
|
||||||
|
.map { (k, v) -> "$k=$v" }
|
||||||
|
.joinToString("&")
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// List<Map> 签名内容
|
||||||
|
// =========================
|
||||||
|
fun getListSignatureContent(mapList: List<Map<String, Any?>>?): String? {
|
||||||
|
if (mapList == null) return null
|
||||||
|
|
||||||
|
return mapList
|
||||||
|
.mapNotNull { getSignatureContent(it) }
|
||||||
|
.sorted()
|
||||||
|
.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package com.bbit.ticket.utils.net
|
||||||
|
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
import java.util.*
|
||||||
|
import javax.crypto.Cipher
|
||||||
|
import javax.crypto.SecretKey
|
||||||
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
|
//
|
||||||
|
// Source code recreated from a .class file by IntelliJ IDEA
|
||||||
|
// (powered by FernFlower decompiler)
|
||||||
|
//
|
||||||
|
|
||||||
|
object SecurityUtil {
|
||||||
|
private const val ALGORITHM_3DES = "DESede"
|
||||||
|
val DEFAULT_CHARSET: Charset = Charset.forName("UTF-8")
|
||||||
|
|
||||||
|
fun encrypt3DES(encryptPassword: String, encryptByte: ByteArray): ByteArray? {
|
||||||
|
try {
|
||||||
|
val cipher = init3DES(encryptPassword, 1)
|
||||||
|
val doFinal = cipher.doFinal(encryptByte)
|
||||||
|
return doFinal
|
||||||
|
} catch (var4: Exception) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun encrypt3DES(encryptPassword: String, encryptStr: String): String? {
|
||||||
|
try {
|
||||||
|
val cipher = init3DES(encryptPassword, 1)
|
||||||
|
val enBytes = cipher.doFinal(encryptStr.toByteArray(DEFAULT_CHARSET))
|
||||||
|
return Base64.getEncoder().encodeToString(enBytes)
|
||||||
|
} catch (var4: Exception) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decrypt3DES(decryptPassword: String, decryptByte: ByteArray): ByteArray? {
|
||||||
|
try {
|
||||||
|
val cipher = init3DES(decryptPassword, 2)
|
||||||
|
val doFinal = cipher.doFinal(decryptByte)
|
||||||
|
return doFinal
|
||||||
|
} catch (var4: Exception) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decrypt3DES(decryptPassword: String, decryptString: String?): String? {
|
||||||
|
try {
|
||||||
|
val cipher = init3DES(decryptPassword, 2)
|
||||||
|
val deBytes = cipher.doFinal(Base64.getDecoder().decode(decryptString))
|
||||||
|
return String(deBytes, DEFAULT_CHARSET)
|
||||||
|
} catch (var4: Exception) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
private fun init3DES(decryptPassword: String, cipherMode: Int): Cipher {
|
||||||
|
val deskey: SecretKey = SecretKeySpec(decryptPassword.toByteArray(), "DESede")
|
||||||
|
val cipher = Cipher.getInstance("DESede")
|
||||||
|
cipher.init(cipherMode, deskey)
|
||||||
|
return cipher
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user