初始化天卡开放平台后端项目
包含完整功能模块: - 设备激活(直销激活码模式 + 代销免激活码模式) - 微信支付 + 天卡订单 - 天卡池管理 + 异步橘视厅SDK激活 - 第三方合作伙伴接口(代销模式) - 签名鉴权(appKey/appSecret + nonce防重放) - 天卡消耗(事务 + 幂等 + 并发控制) - 媒资接口(三级授权过滤) - 审计日志 - 定价策略(3层:门店 > 渠道 > 平台) - 分账(微信分账 + 分润规则) - Docker部署(含Redis容器)
This commit is contained in:
43
src/main/resources/application-dev.yml
Normal file
43
src/main/resources/application-dev.yml
Normal file
@@ -0,0 +1,43 @@
|
||||
spring:
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
druid:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: ${DB_URL:jdbc:mysql://127.0.0.1:3306/dev-0418?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai}
|
||||
username: ${DB_USERNAME:root}
|
||||
password: ${DB_PASSWORD:r2356510}
|
||||
initial-size: 10
|
||||
max-active: 100
|
||||
min-idle: 10
|
||||
max-wait: 60000
|
||||
pool-prepared-statements: true
|
||||
max-pool-prepared-statement-per-connection-size: 20
|
||||
time-between-eviction-runs-millis: 60000
|
||||
min-evictable-idle-time-millis: 300000
|
||||
test-while-idle: true
|
||||
test-on-borrow: false
|
||||
test-on-return: false
|
||||
stat-view-servlet:
|
||||
enabled: true
|
||||
url-pattern: /druid/*
|
||||
filter:
|
||||
stat:
|
||||
log-slow-sql: true
|
||||
slow-sql-millis: 1000
|
||||
merge-sql: false
|
||||
wall:
|
||||
enabled: false
|
||||
|
||||
redis:
|
||||
open: ${REDIS_OPEN:true}
|
||||
database: ${REDIS_DATABASE:0}
|
||||
host: ${REDIS_HOST:127.0.0.1}
|
||||
port: ${REDIS_PORT:6379}
|
||||
password: ${REDIS_PASSWORD:}
|
||||
timeout: ${REDIS_TIMEOUT:6000ms}
|
||||
jedis:
|
||||
pool:
|
||||
max-active: ${REDIS_POOL_MAX_ACTIVE:1000}
|
||||
max-wait: ${REDIS_POOL_MAX_WAIT:-1ms}
|
||||
max-idle: ${REDIS_POOL_MAX_IDLE:10}
|
||||
min-idle: ${REDIS_POOL_MIN_IDLE:5}
|
||||
101
src/main/resources/application.yml
Normal file
101
src/main/resources/application.yml
Normal file
@@ -0,0 +1,101 @@
|
||||
# Tomcat
|
||||
server:
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
threads:
|
||||
max: 1000
|
||||
min-spare: 30
|
||||
connection-timeout: 5000ms
|
||||
basedir: .
|
||||
accesslog:
|
||||
enabled: true
|
||||
pattern: '%t %a "%r" %s %b %Dms'
|
||||
suffix: .log
|
||||
prefix: access_log
|
||||
directory: logs
|
||||
port: ${SERVER_PORT:8084}
|
||||
servlet:
|
||||
context-path: /
|
||||
|
||||
spring:
|
||||
# 环境 dev|test|prod,通过环境变量 SPRING_PROFILES_ACTIVE 指定
|
||||
profiles:
|
||||
active: ${SPRING_PROFILES_ACTIVE:dev}
|
||||
# jackson时间格式化
|
||||
jackson:
|
||||
time-zone: Asia/Shanghai
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 100MB
|
||||
max-request-size: 100MB
|
||||
enabled: true
|
||||
|
||||
mvc:
|
||||
throw-exception-if-no-handler-found: true
|
||||
pathmatch:
|
||||
matching-strategy: ANT_PATH_MATCHER
|
||||
|
||||
#mybatis
|
||||
mybatis-plus:
|
||||
mapper-locations: classpath*:/mapper/**/*.xml
|
||||
#实体扫描,多个package用逗号或者分号分隔
|
||||
typeAliasesPackage: io.as.modules.*.entity,io.as.modules.*.entity,io.as.modules.device.entity,io.as.modules.jst.entity
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: AUTO
|
||||
logic-delete-value: -1
|
||||
logic-not-delete-value: 0
|
||||
banner: false
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
cache-enabled: false
|
||||
call-setters-on-nulls: true
|
||||
jdbc-type-for-null: 'null'
|
||||
|
||||
renren:
|
||||
redis:
|
||||
open: ${REDIS_OPEN:true}
|
||||
jwt:
|
||||
secret: ${JWT_SECRET:daycard-openapi-secret-change-in-prod-2026}
|
||||
expire: ${JWT_EXPIRE:604800}
|
||||
header: ${JWT_HEADER:token}
|
||||
api:
|
||||
device-sign-secret: ${API_SIGN_SECRET:daycard-partner-sign-secret-change-in-prod}
|
||||
# 天卡业务配置
|
||||
daycard:
|
||||
# 订单超时时间(分钟)
|
||||
order-timeout-minutes: ${DAYCARD_ORDER_TIMEOUT:15}
|
||||
# 天卡有效期(小时)
|
||||
card-valid-hours: ${DAYCARD_VALID_HOURS:24}
|
||||
# 超时取消任务每次处理数量
|
||||
cancel-batch-size: ${DAYCARD_CANCEL_BATCH:200}
|
||||
# App UI 资源配置(顶栏按钮图标、登录背景图)
|
||||
ui:
|
||||
logo-url: ${UI_LOGO_URL:https://daycard.oss-cn-hangzhou.aliyuncs.com/logo.png}
|
||||
login-bg-url: ${UI_LOGIN_BG_URL:https://daycard.oss-cn-hangzhou.aliyuncs.com/login_bg.png}
|
||||
search-normal-url: ${UI_SEARCH_NORMAL:https://daycard.oss-cn-hangzhou.aliyuncs.com/search_normal.png}
|
||||
search-focus-url: ${UI_SEARCH_FOCUS:https://daycard.oss-cn-hangzhou.aliyuncs.com/search_focus.png}
|
||||
help-normal-url: ${UI_HELP_NORMAL:https://daycard.oss-cn-hangzhou.aliyuncs.com/help_normal.png}
|
||||
help-focus-url: ${UI_HELP_FOCUS:https://daycard.oss-cn-hangzhou.aliyuncs.com/help_focus.png}
|
||||
# 微信支付配置
|
||||
wechat:
|
||||
pay:
|
||||
app-id: ${WECHAT_PAY_APP_ID:}
|
||||
mch-id: ${WECHAT_PAY_MCH_ID:}
|
||||
api-v3-key: ${WECHAT_PAY_API_V3_KEY:}
|
||||
cert-serial-no: ${WECHAT_PAY_CERT_SERIAL_NO:}
|
||||
private-key-path: ${WECHAT_PAY_PRIVATE_KEY_PATH:}
|
||||
notify-url: ${WECHAT_PAY_NOTIFY_URL:}
|
||||
|
||||
# 橘视厅媒资接口配置(对应 Android 端 ApiConfig.kt)
|
||||
jetsen:
|
||||
media:
|
||||
base-url: ${JETSEN_MEDIA_BASE_URL:https://servhub-ep03.future.jetsentv.com/}
|
||||
path-prefix: ${JETSEN_MEDIA_PATH_PREFIX:/IPTVPortalInterface}
|
||||
sp-token: ${JETSEN_MEDIA_SP_TOKEN:9006874392099a89506142c6df9e3314}
|
||||
portal-code: ${JETSEN_MEDIA_PORTAL_CODE:PCODE-JC}
|
||||
sign-key: ${JETSEN_MEDIA_SIGN_KEY:alltvjs}
|
||||
channel-id: ${JETSEN_MEDIA_CHANNEL_ID:001}
|
||||
export-page-size: ${JETSEN_MEDIA_EXPORT_PAGE_SIZE:50}
|
||||
export-max-rows: ${JETSEN_MEDIA_EXPORT_MAX_ROWS:5000}
|
||||
47
src/main/resources/logback-spring.xml
Executable file
47
src/main/resources/logback-spring.xml
Executable file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<include resource="org/springframework/boot/logging/logback/base.xml" />
|
||||
|
||||
<!-- 日志文件输出 -->
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>logs/app.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework.web" level="INFO"/>
|
||||
<logger name="org.springboot.sample" level="TRACE" />
|
||||
|
||||
<!-- 开发、测试环境 -->
|
||||
<springProfile name="dev,test">
|
||||
<logger name="org.springframework.web" level="DEBUG"/>
|
||||
<logger name="org.springframework.web.servlet.DispatcherServlet" level="DEBUG"/>
|
||||
<logger name="org.springboot.sample" level="INFO" />
|
||||
<logger name="io.as" level="DEBUG" />
|
||||
<logger name="com.baomidou.mybatisplus" level="DEBUG" />
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE" />
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
<!-- 生产环境:业务代码保持 ERROR,不输出 MyBatis SQL -->
|
||||
<springProfile name="prod">
|
||||
<logger name="org.springframework.web" level="ERROR"/>
|
||||
<logger name="org.springboot.sample" level="ERROR" />
|
||||
<logger name="io.as" level="ERROR" />
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE" />
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
</configuration>
|
||||
21
src/main/resources/mapper/daycard/DayCardPoolDao.xml
Normal file
21
src/main/resources/mapper/daycard/DayCardPoolDao.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="io.as.modules.daycard.dao.DayCardPoolDao">
|
||||
|
||||
<!-- 使用 FOR UPDATE SKIP LOCKED 抢占一张未使用的天卡,避免并发下单分配到同一张卡 -->
|
||||
<select id="selectOneForUpdateSkipLocked" resultType="io.as.modules.daycard.entity.DayCardPoolEntity">
|
||||
SELECT * FROM day_card_pool
|
||||
WHERE delete_flag = 'UNDEL'
|
||||
AND (
|
||||
status = 'UNUSED'
|
||||
OR (status = 'REUSABLE' AND expire_at > NOW())
|
||||
)
|
||||
ORDER BY
|
||||
CASE WHEN status = 'REUSABLE' THEN 0 ELSE 1 END,
|
||||
id ASC
|
||||
LIMIT 1
|
||||
FOR UPDATE SKIP LOCKED
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
5
src/main/resources/mapper/device/CardDeviceBindingDao.xml
Executable file
5
src/main/resources/mapper/device/CardDeviceBindingDao.xml
Executable file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="io.as.modules.device.dao.CardDeviceBindingDao">
|
||||
</mapper>
|
||||
351
src/main/resources/mapper/device/DeviceDao.xml
Executable file
351
src/main/resources/mapper/device/DeviceDao.xml
Executable file
@@ -0,0 +1,351 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="io.as.modules.device.dao.DeviceDao">
|
||||
|
||||
<sql id="deviceColumns">
|
||||
d.id, d.store_org_id, d.mac, d.device_sn, d.wifi_mac, d.device_uid, d.identifier_strategy,
|
||||
d.device_name, d.device_model, d.ver_code, d.ver_name, d.android_version, d.room_no,
|
||||
d.last_heartbeat, d.remark, d.status, d.delete_flag, d.created_at, d.updated_at
|
||||
</sql>
|
||||
|
||||
<select id="selectDevicePage" resultType="io.as.modules.device.entity.DeviceEntity">
|
||||
SELECT
|
||||
<include refid="deviceColumns"/>
|
||||
FROM device d
|
||||
<where>
|
||||
d.delete_flag = 'UNDEL'
|
||||
AND d.store_org_id IN
|
||||
<foreach collection="storeOrgIds" item="sid" open="(" separator="," close=")">
|
||||
#{sid}
|
||||
</foreach>
|
||||
<if test="mac != null and mac != ''">
|
||||
AND d.mac LIKE CONCAT('%', #{mac}, '%')
|
||||
</if>
|
||||
<if test="cardNo != null and cardNo != ''">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND c.card_no LIKE CONCAT('%', #{cardNo}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="cardStatus != null and cardStatus != ''">
|
||||
<choose>
|
||||
<!-- 与 DeviceImportUtils.resolveCardDisplayStatus 及 DeviceDao 各分支一致 -->
|
||||
<when test="'UNBOUND'.equals(cardStatus)">
|
||||
AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND c.usage_status = 'UNUSED'
|
||||
AND (c.allocation_scene IS NULL OR c.allocation_scene != 'PRE_IMPORT')
|
||||
)
|
||||
)
|
||||
</when>
|
||||
<!-- 预置:仅预置导入且卡未激活(BOUND),与 allocation_scene / bind_source 一致 -->
|
||||
<when test="'PRESET'.equals(cardStatus)">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND c.usage_status = 'BOUND'
|
||||
AND (c.allocation_scene = 'PRE_IMPORT' OR b.bind_source = 'PRE_IMPORT')
|
||||
)
|
||||
</when>
|
||||
<!-- 待激活:已绑卡仍为 BOUND,且非预置导入(如激活码池) -->
|
||||
<when test="'PENDING_ACTIVATION'.equals(cardStatus)">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND c.usage_status = 'BOUND'
|
||||
AND NOT (c.allocation_scene = 'PRE_IMPORT' OR b.bind_source = 'PRE_IMPORT')
|
||||
)
|
||||
</when>
|
||||
<!-- 已激活:仅 usage_status=ACTIVE,且有效截止时间未过期(expire_at 优先,否则 third_expire_at,与列表 resolveCardDisplayStatus 一致) -->
|
||||
<when test="'ACTIVE'.equals(cardStatus)">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND c.usage_status = 'ACTIVE'
|
||||
AND (
|
||||
COALESCE(c.expire_at, c.third_expire_at) IS NULL
|
||||
OR COALESCE(c.expire_at, c.third_expire_at) >= NOW()
|
||||
)
|
||||
)
|
||||
</when>
|
||||
<!-- 待迁移:与「已激活」分列筛选 -->
|
||||
<when test="'WAITING_MIGRATION'.equals(cardStatus)">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND c.usage_status = 'WAITING_MIGRATION'
|
||||
AND (
|
||||
COALESCE(c.expire_at, c.third_expire_at) IS NULL
|
||||
OR COALESCE(c.expire_at, c.third_expire_at) >= NOW()
|
||||
)
|
||||
)
|
||||
</when>
|
||||
<!-- 过期:与 DeviceImportUtils.resolveCardDisplayStatus 一致;BOUND/UNUSED/激活中/激活失败不因仅日期判为列表「过期」 -->
|
||||
<when test="'EXPIRED'.equals(cardStatus)">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND (
|
||||
c.usage_status = 'EXPIRED'
|
||||
OR (
|
||||
COALESCE(c.expire_at, c.third_expire_at) IS NOT NULL
|
||||
AND COALESCE(c.expire_at, c.third_expire_at) < NOW()
|
||||
AND (c.usage_status IS NULL OR c.usage_status NOT IN ('UNUSED', 'BOUND', 'ACTIVATING', 'ACTIVATION_FAILED', 'EXPIRED'))
|
||||
)
|
||||
)
|
||||
)
|
||||
</when>
|
||||
<!-- 激活中:ACTIVATING,或 BOUND 且未因 expire_at 被判为过期 -->
|
||||
<when test="'ACTIVATING'.equals(cardStatus)">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND (
|
||||
c.usage_status = 'ACTIVATING'
|
||||
)
|
||||
)
|
||||
</when>
|
||||
<otherwise>
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND c.usage_status = #{cardStatus}
|
||||
)
|
||||
</otherwise>
|
||||
</choose>
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY d.id DESC
|
||||
</select>
|
||||
|
||||
<select id="selectDeviceListForExport" resultType="io.as.modules.device.entity.DeviceEntity">
|
||||
SELECT
|
||||
<include refid="deviceColumns"/>
|
||||
FROM device d
|
||||
<where>
|
||||
d.delete_flag = 'UNDEL'
|
||||
AND d.store_org_id IN
|
||||
<foreach collection="storeOrgIds" item="sid" open="(" separator="," close=")">
|
||||
#{sid}
|
||||
</foreach>
|
||||
<if test="mac != null and mac != ''">
|
||||
AND d.mac LIKE CONCAT('%', #{mac}, '%')
|
||||
</if>
|
||||
<if test="cardNo != null and cardNo != ''">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND c.card_no LIKE CONCAT('%', #{cardNo}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="cardStatus != null and cardStatus != ''">
|
||||
<choose>
|
||||
<when test="'UNBOUND'.equals(cardStatus)">
|
||||
AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
)
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND c.usage_status = 'UNUSED'
|
||||
AND (c.allocation_scene IS NULL OR c.allocation_scene != 'PRE_IMPORT')
|
||||
)
|
||||
)
|
||||
</when>
|
||||
<when test="'PRESET'.equals(cardStatus)">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND c.usage_status = 'BOUND'
|
||||
AND (c.allocation_scene = 'PRE_IMPORT' OR b.bind_source = 'PRE_IMPORT')
|
||||
)
|
||||
</when>
|
||||
<when test="'PENDING_ACTIVATION'.equals(cardStatus)">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND c.usage_status = 'BOUND'
|
||||
AND NOT (c.allocation_scene = 'PRE_IMPORT' OR b.bind_source = 'PRE_IMPORT')
|
||||
)
|
||||
</when>
|
||||
<when test="'ACTIVE'.equals(cardStatus)">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND c.usage_status = 'ACTIVE'
|
||||
AND (
|
||||
COALESCE(c.expire_at, c.third_expire_at) IS NULL
|
||||
OR COALESCE(c.expire_at, c.third_expire_at) >= NOW()
|
||||
)
|
||||
)
|
||||
</when>
|
||||
<when test="'WAITING_MIGRATION'.equals(cardStatus)">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND c.usage_status = 'WAITING_MIGRATION'
|
||||
AND (
|
||||
COALESCE(c.expire_at, c.third_expire_at) IS NULL
|
||||
OR COALESCE(c.expire_at, c.third_expire_at) >= NOW()
|
||||
)
|
||||
)
|
||||
</when>
|
||||
<when test="'EXPIRED'.equals(cardStatus)">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND (
|
||||
c.usage_status = 'EXPIRED'
|
||||
OR (
|
||||
COALESCE(c.expire_at, c.third_expire_at) IS NOT NULL
|
||||
AND COALESCE(c.expire_at, c.third_expire_at) < NOW()
|
||||
AND (c.usage_status IS NULL OR c.usage_status NOT IN ('UNUSED', 'BOUND', 'ACTIVATING', 'ACTIVATION_FAILED', 'EXPIRED'))
|
||||
)
|
||||
)
|
||||
)
|
||||
</when>
|
||||
<when test="'ACTIVATING'.equals(cardStatus)">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND (
|
||||
c.usage_status = 'ACTIVATING'
|
||||
)
|
||||
)
|
||||
</when>
|
||||
<otherwise>
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.device_id = d.id AND b.unbind_at IS NULL AND b.status IN ('BOUND', 'STANDBY')
|
||||
AND c.usage_status = #{cardStatus}
|
||||
)
|
||||
</otherwise>
|
||||
</choose>
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY d.id DESC
|
||||
</select>
|
||||
|
||||
<!-- 临期年卡设备:BOUND 上年卡 ACTIVE 且有效截止在今后 30 天内;续费已下挂 STANDBY 新卡的设备排除 -->
|
||||
<!-- 使用 JOIN 聚合替代 EXISTS + ORDER BY 标量子查询,避免 MP 分页 COUNT 优化(JSqlParser)执行失败 -->
|
||||
<select id="selectExpiringYearDevicePage" resultType="io.as.modules.device.entity.DeviceEntity">
|
||||
SELECT
|
||||
<include refid="deviceColumns"/>
|
||||
FROM device d
|
||||
INNER JOIN (
|
||||
SELECT b.device_id AS did,
|
||||
MIN(COALESCE(c.expire_at, c.third_expire_at)) AS eff_expire
|
||||
FROM card_device_binding b
|
||||
INNER JOIN card c ON c.id = b.card_id AND c.delete_flag = 'UNDEL'
|
||||
WHERE b.unbind_at IS NULL AND b.status = 'BOUND'
|
||||
AND c.card_type = 'YEAR'
|
||||
AND c.usage_status = 'ACTIVE'
|
||||
AND COALESCE(c.expire_at, c.third_expire_at) IS NOT NULL
|
||||
AND COALESCE(c.expire_at, c.third_expire_at) > #{now}
|
||||
AND COALESCE(c.expire_at, c.third_expire_at) <= #{windowEnd}
|
||||
GROUP BY b.device_id
|
||||
) exp ON exp.did = d.id
|
||||
<where>
|
||||
d.delete_flag = 'UNDEL'
|
||||
AND d.store_org_id IN
|
||||
<foreach collection="storeOrgIds" item="sid" open="(" separator="," close=")">
|
||||
#{sid}
|
||||
</foreach>
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM card_device_binding sb
|
||||
WHERE sb.device_id = d.id AND sb.unbind_at IS NULL AND sb.status = 'STANDBY'
|
||||
)
|
||||
</where>
|
||||
ORDER BY exp.eff_expire ASC, d.id DESC
|
||||
</select>
|
||||
|
||||
<select id="selectDuplicateMacDeviceSnPage" resultType="io.as.modules.device.entity.DeviceEntity">
|
||||
SELECT
|
||||
<include refid="deviceColumns"/>
|
||||
FROM device d
|
||||
INNER JOIN (
|
||||
SELECT mac, device_sn
|
||||
FROM device
|
||||
WHERE delete_flag = 'UNDEL'
|
||||
GROUP BY mac, device_sn
|
||||
HAVING COUNT(*) > 1
|
||||
) dup ON d.mac = dup.mac AND d.device_sn = dup.device_sn AND d.delete_flag = 'UNDEL'
|
||||
<where>
|
||||
<choose>
|
||||
<when test="storeOrgIds != null and storeOrgIds.size() > 0">
|
||||
AND (d.store_org_id IS NULL OR d.store_org_id IN
|
||||
<foreach collection="storeOrgIds" item="sid" open="(" separator="," close=")">
|
||||
#{sid}
|
||||
</foreach>
|
||||
)
|
||||
</when>
|
||||
<otherwise>
|
||||
AND d.store_org_id IS NULL
|
||||
</otherwise>
|
||||
</choose>
|
||||
<if test="mac != null and mac != ''">
|
||||
AND d.mac LIKE CONCAT('%', #{mac}, '%')
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY d.mac ASC, d.device_sn ASC, d.id DESC
|
||||
</select>
|
||||
|
||||
<select id="selectDuplicateMacPage" resultType="io.as.modules.device.entity.DeviceEntity">
|
||||
SELECT
|
||||
<include refid="deviceColumns"/>
|
||||
FROM device d
|
||||
INNER JOIN (
|
||||
SELECT mac
|
||||
FROM device
|
||||
WHERE delete_flag = 'UNDEL'
|
||||
GROUP BY mac
|
||||
HAVING COUNT(*) > 1
|
||||
) dup ON d.mac = dup.mac AND d.delete_flag = 'UNDEL'
|
||||
<where>
|
||||
<choose>
|
||||
<when test="storeOrgIds != null and storeOrgIds.size() > 0">
|
||||
AND (d.store_org_id IS NULL OR d.store_org_id IN
|
||||
<foreach collection="storeOrgIds" item="sid" open="(" separator="," close=")">
|
||||
#{sid}
|
||||
</foreach>
|
||||
)
|
||||
</when>
|
||||
<otherwise>
|
||||
AND d.store_org_id IS NULL
|
||||
</otherwise>
|
||||
</choose>
|
||||
<if test="mac != null and mac != ''">
|
||||
AND d.mac LIKE CONCAT('%', #{mac}, '%')
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY d.mac ASC, d.device_sn ASC, d.id DESC
|
||||
</select>
|
||||
</mapper>
|
||||
53
src/main/resources/mapper/jst/ApiJstCallLogDao.xml
Executable file
53
src/main/resources/mapper/jst/ApiJstCallLogDao.xml
Executable file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="io.as.modules.jst.dao.ApiJstCallLogDao">
|
||||
|
||||
<select id="selectJstCallLogPage" resultType="io.as.modules.sys.vo.ApiJstCallLogPageRowVo">
|
||||
SELECT
|
||||
l.id,
|
||||
l.api_path AS apiPath,
|
||||
l.request_method AS requestMethod,
|
||||
l.request_body AS requestBody,
|
||||
l.response_code AS responseCode,
|
||||
l.response_body AS responseBody,
|
||||
l.success,
|
||||
l.duration,
|
||||
l.data_id AS dataId,
|
||||
l.request_headers AS requestHeaders,
|
||||
l.client_ip AS clientIp,
|
||||
l.error_message AS errorMessage,
|
||||
l.create_time AS createTime,
|
||||
d.mac,
|
||||
d.device_sn AS deviceSn,
|
||||
d.device_uid AS deviceUid,
|
||||
o.id AS storeOrgId,
|
||||
o.name AS storeOrgName,
|
||||
o.code AS storeOrgCode,
|
||||
o.type AS storeOrgType
|
||||
FROM api_jst_call_log l
|
||||
LEFT JOIN device d ON l.data_id = d.id AND (d.delete_flag IS NULL OR d.delete_flag != 'DEL')
|
||||
LEFT JOIN sys_org o ON d.store_org_id = o.id AND (o.delete_flag IS NULL OR o.delete_flag != 'DEL')
|
||||
<where>
|
||||
<if test="mac != null and mac != ''">
|
||||
AND (
|
||||
d.mac LIKE CONCAT('%', #{mac}, '%')
|
||||
OR IFNULL(l.request_body, '') LIKE CONCAT('%', #{mac}, '%')
|
||||
OR IFNULL(l.request_headers, '') LIKE CONCAT('%', #{mac}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="beginTime != null">
|
||||
AND l.create_time >= #{beginTime}
|
||||
</if>
|
||||
<if test="endTime != null">
|
||||
AND l.create_time <= #{endTime}
|
||||
</if>
|
||||
<if test="apiPathKw != null and apiPathKw != ''">
|
||||
AND IFNULL(l.api_path, '') LIKE CONCAT('%', #{apiPathKw}, '%')
|
||||
</if>
|
||||
<if test="bodyKw != null and bodyKw != ''">
|
||||
AND IFNULL(l.request_body, '') LIKE CONCAT('%', #{bodyKw}, '%')
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
</mapper>
|
||||
5
src/main/resources/mapper/sys/StoreProfileDao.xml
Executable file
5
src/main/resources/mapper/sys/StoreProfileDao.xml
Executable file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="io.as.modules.sys.dao.StoreProfileDao">
|
||||
</mapper>
|
||||
19
src/main/resources/mapper/sys/SysConfigDao.xml
Normal file
19
src/main/resources/mapper/sys/SysConfigDao.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="io.as.modules.sys.dao.SysConfigDao">
|
||||
|
||||
<select id="queryByKey" resultType="io.as.modules.sys.entity.SysConfigEntity">
|
||||
SELECT id, param_key, param_value, remark
|
||||
FROM sys_config
|
||||
WHERE param_key = #{paramKey}
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<update id="updateValueByKey">
|
||||
UPDATE sys_config
|
||||
SET param_value = #{paramValue}
|
||||
WHERE param_key = #{paramKey}
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
10
src/main/resources/mapper/sys/SysMenuDao.xml
Normal file
10
src/main/resources/mapper/sys/SysMenuDao.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="io.as.modules.sys.dao.SysMenuDao">
|
||||
<select id="queryListParentId" resultType="io.as.modules.sys.entity.SysMenuEntity">
|
||||
select * from sys_menu where parent_id = #{parentId} order by order_num asc
|
||||
</select>
|
||||
<select id="queryNotButtonList" resultType="io.as.modules.sys.entity.SysMenuEntity">
|
||||
select * from sys_menu where type != 2 order by order_num asc
|
||||
</select>
|
||||
</mapper>
|
||||
56
src/main/resources/mapper/sys/SysOrgDao.xml
Executable file
56
src/main/resources/mapper/sys/SysOrgDao.xml
Executable file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="io.as.modules.sys.dao.SysOrgDao">
|
||||
|
||||
<!-- 根据父ID查询子组织列表(未删除) -->
|
||||
<select id="queryListByParentId" resultType="io.as.modules.sys.entity.SysOrgEntity">
|
||||
select * from sys_org
|
||||
where delete_flag = 'UNDEL'
|
||||
<choose>
|
||||
<when test="parentId == null">
|
||||
and parent_id is null
|
||||
</when>
|
||||
<otherwise>
|
||||
and parent_id = #{parentId}
|
||||
</otherwise>
|
||||
</choose>
|
||||
order by order_no asc, id asc
|
||||
</select>
|
||||
|
||||
<!-- 该组织及子级拥有的卡数量(org_id 在子树内) -->
|
||||
<select id="countCardInSubtree" resultType="java.lang.Long">
|
||||
SELECT COUNT(*) FROM card c
|
||||
WHERE c.delete_flag = 'UNDEL'
|
||||
AND c.org_id IN (
|
||||
SELECT s.id FROM sys_org s
|
||||
WHERE s.delete_flag = 'UNDEL'
|
||||
<choose>
|
||||
<when test="mpath != null and mpath != ''">
|
||||
AND (s.id = #{orgId} OR (s.mpath IS NOT NULL AND s.mpath LIKE CONCAT(#{mpath}, '%')))
|
||||
</when>
|
||||
<otherwise>
|
||||
AND s.id = #{orgId}
|
||||
</otherwise>
|
||||
</choose>
|
||||
)
|
||||
</select>
|
||||
|
||||
<!-- 已分配到该组织及子级下门店的卡数量 -->
|
||||
<select id="countStoreAllocatedCardInSubtree" resultType="java.lang.Long">
|
||||
SELECT COUNT(*) FROM card c
|
||||
WHERE c.delete_flag = 'UNDEL'
|
||||
AND c.org_id IN (
|
||||
SELECT s.id FROM sys_org s
|
||||
WHERE s.delete_flag = 'UNDEL' AND s.type = 'STORE'
|
||||
<choose>
|
||||
<when test="mpath != null and mpath != ''">
|
||||
AND (s.id = #{orgId} OR (s.mpath IS NOT NULL AND s.mpath LIKE CONCAT(#{mpath}, '%')))
|
||||
</when>
|
||||
<otherwise>
|
||||
AND s.id = #{orgId}
|
||||
</otherwise>
|
||||
</choose>
|
||||
)
|
||||
</select>
|
||||
</mapper>
|
||||
7
src/main/resources/mapper/sys/SysRoleDao.xml
Normal file
7
src/main/resources/mapper/sys/SysRoleDao.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="io.as.modules.sys.dao.SysRoleDao">
|
||||
<select id="queryRoleIdList" resultType="long">
|
||||
select id from sys_role
|
||||
</select>
|
||||
</mapper>
|
||||
13
src/main/resources/mapper/sys/SysRoleMenuDao.xml
Normal file
13
src/main/resources/mapper/sys/SysRoleMenuDao.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="io.as.modules.sys.dao.SysRoleMenuDao">
|
||||
<select id="queryMenuIdList" resultType="long">
|
||||
select menu_id from sys_role_menu where role_id = #{value}
|
||||
</select>
|
||||
<delete id="deleteBatch">
|
||||
delete from sys_role_menu where role_id in
|
||||
<foreach item="roleId" collection="array" open="(" separator="," close=")">
|
||||
#{roleId}
|
||||
</foreach>
|
||||
</delete>
|
||||
</mapper>
|
||||
18
src/main/resources/mapper/sys/SysUserDao.xml
Normal file
18
src/main/resources/mapper/sys/SysUserDao.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="io.as.modules.sys.dao.SysUserDao">
|
||||
<select id="queryAllPerms" resultType="string">
|
||||
select m.perms from sys_user_role ur
|
||||
LEFT JOIN sys_role_menu rm on ur.role_id = rm.role_id
|
||||
LEFT JOIN sys_menu m on rm.menu_id = m.id
|
||||
where ur.user_id = #{userId}
|
||||
</select>
|
||||
<select id="queryAllMenuId" resultType="long">
|
||||
select distinct rm.menu_id from sys_user_role ur
|
||||
LEFT JOIN sys_role_menu rm on ur.role_id = rm.role_id
|
||||
where ur.user_id = #{userId}
|
||||
</select>
|
||||
<select id="queryByUserName" resultType="io.as.modules.sys.entity.SysUserEntity">
|
||||
select * from sys_user where username = #{username}
|
||||
</select>
|
||||
</mapper>
|
||||
13
src/main/resources/mapper/sys/SysUserRoleDao.xml
Normal file
13
src/main/resources/mapper/sys/SysUserRoleDao.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="io.as.modules.sys.dao.SysUserRoleDao">
|
||||
<delete id="deleteBatch">
|
||||
delete from sys_user_role where role_id in
|
||||
<foreach item="roleId" collection="array" open="(" separator="," close=")">
|
||||
#{roleId}
|
||||
</foreach>
|
||||
</delete>
|
||||
<select id="queryRoleIdList" resultType="long">
|
||||
select role_id from sys_user_role where user_id = #{value}
|
||||
</select>
|
||||
</mapper>
|
||||
24
src/main/resources/mapper/sys/SysUserTokenDao.xml
Normal file
24
src/main/resources/mapper/sys/SysUserTokenDao.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="io.as.modules.sys.dao.SysUserTokenDao">
|
||||
<select id="queryByToken" resultType="io.as.modules.sys.entity.SysUserTokenEntity">
|
||||
select * from sys_user_token where token = #{value}
|
||||
</select>
|
||||
<select id="queryByRefreshToken" resultType="io.as.modules.sys.entity.SysUserTokenEntity">
|
||||
select * from sys_user_token where refresh_token = #{value}
|
||||
</select>
|
||||
<select id="queryByPreviousRefreshToken" resultType="io.as.modules.sys.entity.SysUserTokenEntity">
|
||||
select * from sys_user_token where previous_refresh_token = #{value}
|
||||
</select>
|
||||
<update id="updateTokenRotation">
|
||||
update sys_user_token
|
||||
set token = #{newToken},
|
||||
expire_time = #{newExpireTime},
|
||||
refresh_token = #{newRefreshToken},
|
||||
refresh_expire_time = #{newRefreshExpireTime},
|
||||
previous_refresh_token = #{currentRefreshToken},
|
||||
update_time = #{updateTime}
|
||||
where user_id = #{userId}
|
||||
and refresh_token = #{currentRefreshToken}
|
||||
</update>
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user