初始化天卡开放平台后端项目

包含完整功能模块:
- 设备激活(直销激活码模式 + 代销免激活码模式)
- 微信支付 + 天卡订单
- 天卡池管理 + 异步橘视厅SDK激活
- 第三方合作伙伴接口(代销模式)
  - 签名鉴权(appKey/appSecret + nonce防重放)
  - 天卡消耗(事务 + 幂等 + 并发控制)
  - 媒资接口(三级授权过滤)
  - 审计日志
- 定价策略(3层:门店 > 渠道 > 平台)
- 分账(微信分账 + 分润规则)
- Docker部署(含Redis容器)
This commit is contained in:
zhaogd
2026-07-26 12:34:13 +08:00
commit 30198c7191
349 changed files with 28837 additions and 0 deletions

View 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}

View 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}

View 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>

View 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>

View 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>

View 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) &gt;= 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) &gt;= 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) &lt; 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) &gt;= 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) &gt;= 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) &lt; 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) &gt; #{now}
AND COALESCE(c.expire_at, c.third_expire_at) &lt;= #{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(*) &gt; 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() &gt; 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(*) &gt; 1
) dup ON d.mac = dup.mac AND d.delete_flag = 'UNDEL'
<where>
<choose>
<when test="storeOrgIds != null and storeOrgIds.size() &gt; 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>

View 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 &gt;= #{beginTime}
</if>
<if test="endTime != null">
AND l.create_time &lt;= #{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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>