fix:团队统计修改

This commit is contained in:
yys 2026-04-22 13:01:36 +08:00
parent abbd589b82
commit b1707c8d20
24 changed files with 257 additions and 442 deletions

View File

@ -69,12 +69,12 @@
<el-table-column label="用户ID" align="center" prop="uuid" /> <el-table-column label="用户ID" align="center" prop="uuid" />
<el-table-column label="操作类型" align="center" prop="type" :formatter="formatType" /> <el-table-column label="操作类型" align="center" prop="type" :formatter="formatType" />
<el-table-column <el-table-column
label="变更金额" label="变更积分"
align="center" align="center"
prop="changeAmount" prop="changeAmount"
:formatter="formatChangeAmount" :formatter="formatChangeAmount"
/> />
<el-table-column label="变更后金额" align="center" prop="resultAmount" /> <el-table-column label="变更后积分" align="center" prop="resultAmount" />
<el-table-column label="操作时间" align="center" prop="createTime" /> <el-table-column label="操作时间" align="center" prop="createTime" />
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column label="备注" align="center" prop="remark" />
</el-table> </el-table>
@ -93,11 +93,11 @@
<el-form-item label="用户ID" prop="userId"> <el-form-item label="用户ID" prop="userId">
<el-input v-model="form.userId" placeholder="请输入用户ID" /> <el-input v-model="form.userId" placeholder="请输入用户ID" />
</el-form-item> </el-form-item>
<el-form-item label="变更金额" prop="changeAmount"> <el-form-item label="变更积分" prop="changeAmount">
<el-input v-model="form.changeAmount" placeholder="请输入变更金额" /> <el-input v-model="form.changeAmount" placeholder="请输入变更积分" />
</el-form-item> </el-form-item>
<el-form-item label="变更后金额" prop="resultAmount"> <el-form-item label="变更后积分" prop="resultAmount">
<el-input v-model="form.resultAmount" placeholder="请输入变更后金额" /> <el-input v-model="form.resultAmount" placeholder="请输入变更后积分" />
</el-form-item> </el-form-item>
<el-form-item label="操作类型" prop="type"> <el-form-item label="操作类型" prop="type">
<el-select <el-select

View File

@ -1,19 +1,22 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="96px"> <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="96px">
<el-form-item label="日期" prop="statDate"> <el-form-item label="统计日期">
<el-date-picker <el-date-picker
v-model="queryParams.statDate" v-model="dateRange"
type="date" type="daterange"
value-format="yyyyMMdd" unlink-panels
placeholder="请选择日期" value-format="yyyy-MM-dd"
clearable range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
style="width: 280px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="团队名称" prop="deptId"> <el-form-item label="团队" prop="deptId">
<el-select <el-select
v-model="queryParams.deptId" v-model="queryParams.deptId"
placeholder="请选择团队" placeholder="全部团队(可选)"
clearable clearable
filterable filterable
style="width: 220px" style="width: 220px"
@ -37,12 +40,31 @@
</el-row> </el-row>
<el-table v-loading="loading" :data="dataList"> <el-table v-loading="loading" :data="dataList">
<el-table-column label="日期" align="center" prop="dateKey" /> <el-table-column label="日期" align="center" prop="dateKey" min-width="110" />
<el-table-column label="团队名称" align="center" prop="deptName" /> <el-table-column label="团队ID" align="center" prop="deptId" width="100" />
<el-table-column label="实际充值积分(充值-退款)" align="center" prop="rechargeScore" /> <el-table-column label="团队名称" align="center" prop="deptName" min-width="140" show-overflow-tooltip />
<el-table-column label="消耗积分" align="center" prop="score" /> <el-table-column
<el-table-column label="实际订单数量(成功)" align="center" prop="orderCount" /> label="实际充值积分(充值-退款)"
<el-table-column label="三方消耗tokens数量" align="center" prop="useTokens" /> align="center"
prop="rechargeScore"
min-width="200"
:formatter="formatWesternNumber"
/>
<el-table-column label="消耗积分" align="center" prop="score" width="120" :formatter="formatWesternNumber" />
<el-table-column
label="实际订单数量(成功)"
align="center"
prop="orderCount"
width="160"
:formatter="formatWesternNumber"
/>
<el-table-column
label="三方消耗tokens数量"
align="center"
prop="useTokens"
width="170"
:formatter="formatWesternNumber"
/>
</el-table> </el-table>
<pagination <pagination
@ -64,77 +86,97 @@ export default {
name: "TeamConsumeData", name: "TeamConsumeData",
data() { data() {
return { return {
//
loading: false, loading: false,
//
showSearch: true, showSearch: true,
//
total: 0, total: 0,
//
dataList: [], dataList: [],
//
deptOptions: [], deptOptions: [],
// dateRange: [],
searched: false,
//
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
statDate: null, startDate: null,
endDate: null,
deptId: null deptId: null
}, }
} }
}, },
created() { created() {
this.loadSecondLevelDeptOptions() this.loadSecondLevelDeptOptions()
this.initDefaultDateRange()
this.syncDateRangeToQuery()
this.getList()
}, },
methods: { methods: {
/** 加载二级部门选项 */ /** null/空 显示 0数字按 en-US 千分位(如 99,999,999 */
formatWesternNumber(row, column, cellValue) {
if (cellValue === null || cellValue === undefined || cellValue === "") {
return (0).toLocaleString("en-US")
}
const n = Number(cellValue)
if (Number.isNaN(n)) {
return (0).toLocaleString("en-US")
}
return n.toLocaleString("en-US")
},
buildLastMonthDateRange() {
const end = new Date()
const start = new Date(end.getTime())
start.setMonth(start.getMonth() - 1)
return [this.formatYmd(start), this.formatYmd(end)]
},
formatYmd(d) {
const y = d.getFullYear()
const m = String(d.getMonth() + 1).padStart(2, "0")
const day = String(d.getDate()).padStart(2, "0")
return `${y}-${m}-${day}`
},
initDefaultDateRange() {
this.dateRange = this.buildLastMonthDateRange()
},
loadSecondLevelDeptOptions() { loadSecondLevelDeptOptions() {
listDept({ status: "0" }).then(response => { listDept({ status: "0" }).then(response => {
const allDeptList = response.data || [] const allDeptList = response.data || []
this.deptOptions = allDeptList.filter(item => this.isSecondLevelDept(item)) this.deptOptions = allDeptList.filter(item => this.isSecondLevelDept(item))
}) })
}, },
/** 判断是否为二级部门(一级部门的直属子部门) */
isSecondLevelDept(dept) { isSecondLevelDept(dept) {
if (!dept || !dept.ancestors) { if (!dept || !dept.ancestors) {
return false return false
} }
return String(dept.ancestors).split(",").filter(Boolean).length === 2 return String(dept.ancestors).split(",").filter(Boolean).length === 2
}, },
/** 查询团队每日消耗统计列表 */ syncDateRangeToQuery() {
getList() { if (Array.isArray(this.dateRange) && this.dateRange.length === 2) {
if (!this.searched) { this.queryParams.startDate = this.dateRange[0]
this.loading = false this.queryParams.endDate = this.dateRange[1]
this.dataList = [] } else {
this.total = 0 this.queryParams.startDate = null
return this.queryParams.endDate = null
} }
this.loading = true
listData(this.queryParams).then(response => {
this.dataList = response.rows
this.total = response.total
this.loading = false
})
}, },
/** 搜索按钮操作 */ getList() {
this.syncDateRangeToQuery()
this.loading = true
listData(this.queryParams)
.then(response => {
this.dataList = response.rows
this.total = response.total
})
.finally(() => {
this.loading = false
})
},
handleQuery() { handleQuery() {
if (!this.queryParams.statDate || !this.queryParams.deptId) {
this.$modal.msgWarning("请先填写日期和团队名称后再搜索")
return
}
this.searched = true
this.queryParams.pageNum = 1 this.queryParams.pageNum = 1
this.getList() this.getList()
}, },
/** 重置按钮操作 */
resetQuery() { resetQuery() {
this.initDefaultDateRange()
this.resetForm("queryForm") this.resetForm("queryForm")
this.searched = false this.queryParams.pageNum = 1
this.loading = false this.syncDateRangeToQuery()
this.dataList = [] this.getList()
this.total = 0
} }
} }
} }

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="88px"> <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="70px">
<el-form-item label="订单号" prop="orderNum"> <el-form-item label="订单号" prop="orderNum">
<el-input <el-input
v-model="queryParams.orderNum" v-model="queryParams.orderNum"
@ -18,7 +18,7 @@
/> />
</el-form-item> </el-form-item>
<el-form-item label="类型" prop="orderType"> <el-form-item label="类型" prop="orderType">
<el-select v-model="queryParams.orderType" placeholder="全部" clearable style="width: 140px"> <el-select v-model="queryParams.orderType" placeholder="全部" clearable style="width: 110px">
<el-option v-for="item in orderTypeOptions" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in orderTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -36,12 +36,6 @@
<el-form-item> <el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button <el-button
type="warning" type="warning"
plain plain
@ -50,9 +44,9 @@
@click="handleExport" @click="handleExport"
v-hasPermi="['ai:groupChargeOrder:export']" v-hasPermi="['ai:groupChargeOrder:export']"
>导出</el-button> >导出</el-button>
</el-col> <right-toolbar :showSearch.sync="showSearch" @queryTable="getList" />
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList" /> </el-form-item>
</el-row> </el-form>
<el-table v-loading="loading" :data="groupChargeOrderList"> <el-table v-loading="loading" :data="groupChargeOrderList">
<el-table-column label="订单号" align="center" prop="orderNum" width="200" show-overflow-tooltip /> <el-table-column label="订单号" align="center" prop="orderNum" width="200" show-overflow-tooltip />

View File

@ -64,8 +64,8 @@
<span>{{ typeLabel(scope.row.type) }}</span> <span>{{ typeLabel(scope.row.type) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="变更金额" align="center" prop="changeAmount" width="100" /> <el-table-column label="变更积分" align="center" prop="changeAmount" width="100" />
<el-table-column label="变更后金额" align="center" prop="resultAmount" width="100" /> <el-table-column label="变更后积分" align="center" prop="resultAmount" width="100" />
<el-table-column label="备注" align="center" prop="remark" /> <el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="创建时间" align="center" prop="createTime" width="160"> <el-table-column label="创建时间" align="center" prop="createTime" width="160">
<template slot-scope="scope"> <template slot-scope="scope">

View File

@ -34,8 +34,8 @@
<span>{{ typeLabel(s.row.type) }}</span> <span>{{ typeLabel(s.row.type) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="变更金额" prop="changeAmount" width="100" /> <el-table-column label="变更积分" prop="changeAmount" width="100" />
<el-table-column label="变更后" prop="resultAmount" width="100" /> <el-table-column label="变更后积分" prop="resultAmount" width="100" />
<el-table-column label="时间" width="160"><template slot-scope="s">{{ parseTime(s.row.createTime) }}</template></el-table-column> <el-table-column label="时间" width="160"><template slot-scope="s">{{ parseTime(s.row.createTime) }}</template></el-table-column>
</el-table> </el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" /> <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />

View File

@ -22,11 +22,6 @@
<el-form-item> <el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button <el-button
type="info" type="info"
plain plain
@ -34,9 +29,9 @@
size="mini" size="mini"
@click="toggleExpandAll" @click="toggleExpandAll"
>展开/折叠</el-button> >展开/折叠</el-button>
</el-col> <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> </el-form-item>
</el-row> </el-form>
<el-table <el-table
v-if="refreshTable" v-if="refreshTable"

View File

@ -2,11 +2,16 @@ package com.ruoyi.ai.controller;
import java.util.List; import java.util.List;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize; import javax.validation.Valid;
import com.ruoyi.ai.domain.request.GroupReportDataRequest;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@ -27,8 +32,9 @@ import com.ruoyi.common.core.page.TableDataInfo;
*/ */
@RestController @RestController
@RequestMapping("/ai/data") @RequestMapping("/ai/data")
public class AiVideoReportDataController extends BaseController @Validated
{ public class AiVideoReportDataController extends BaseController {
@Autowired @Autowired
private IAiVideoReportDataService aiVideoReportDataService; private IAiVideoReportDataService aiVideoReportDataService;
@ -37,11 +43,8 @@ public class AiVideoReportDataController extends BaseController
*/ */
@PreAuthorize("@ss.hasPermi('ai:data:list')") @PreAuthorize("@ss.hasPermi('ai:data:list')")
@GetMapping("/list") @GetMapping("/list")
public TableDataInfo list(AiVideoReportData aiVideoReportData) public TableDataInfo list(@Valid @ModelAttribute GroupReportDataRequest query) {
{ List<AiVideoReportData> list = aiVideoReportDataService.selectTeamDailyConsumeList(query);
startPage();
List<AiVideoReportData> list = aiVideoReportDataService.selectTeamDailyConsumeList(
aiVideoReportData.getStatDate(), aiVideoReportData.getDeptId());
return getDataTable(list); return getDataTable(list);
} }
@ -51,9 +54,8 @@ public class AiVideoReportDataController extends BaseController
@PreAuthorize("@ss.hasPermi('ai:data:export')") @PreAuthorize("@ss.hasPermi('ai:data:export')")
@Log(title = "AI视频生成统计数据作为其他统计报的数据源", businessType = BusinessType.EXPORT) @Log(title = "AI视频生成统计数据作为其他统计报的数据源", businessType = BusinessType.EXPORT)
@PostMapping("/export") @PostMapping("/export")
public void export(HttpServletResponse response, AiVideoReportData aiVideoReportData) public void export(HttpServletResponse response, @Valid @RequestBody GroupReportDataRequest query) {
{ List<AiVideoReportData> list = aiVideoReportDataService.selectTeamDailyConsumeList(query);
List<AiVideoReportData> list = aiVideoReportDataService.selectAiVideoReportDataList(aiVideoReportData);
ExcelUtil<AiVideoReportData> util = new ExcelUtil<AiVideoReportData>(AiVideoReportData.class); ExcelUtil<AiVideoReportData> util = new ExcelUtil<AiVideoReportData>(AiVideoReportData.class);
util.exportExcel(response, list, "AI视频生成统计数据作为其他统计报的数据源数据"); util.exportExcel(response, list, "AI视频生成统计数据作为其他统计报的数据源数据");
} }
@ -63,19 +65,7 @@ public class AiVideoReportDataController extends BaseController
*/ */
@PreAuthorize("@ss.hasPermi('ai:data:query')") @PreAuthorize("@ss.hasPermi('ai:data:query')")
@GetMapping(value = "/{id}") @GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") Long id) public AjaxResult getInfo(@PathVariable("id") Long id) {
{
return success(aiVideoReportDataService.selectAiVideoReportDataById(id)); return success(aiVideoReportDataService.selectAiVideoReportDataById(id));
} }
/**
* 新增AI视频生成统计数据作为其他统计报的数据源
*/
@PreAuthorize("@ss.hasPermi('ai:data:add')")
@Log(title = "AI视频生成统计数据作为其他统计报的数据源", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody AiVideoReportData aiVideoReportData)
{
return toAjax(aiVideoReportDataService.insertAiVideoReportData(aiVideoReportData));
}
} }

View File

@ -57,15 +57,15 @@ public class AiBalanceChangeRecord extends BaseEntity {
private Integer type; private Integer type;
/** /**
* 变更金额 * 变更积分
*/ */
@Excel(name = "变更金额") @Excel(name = "变更积分")
private BigDecimal changeAmount; private BigDecimal changeAmount;
/** /**
* 变更后金额 * 变更后金额
*/ */
@Excel(name = "变更后金额") @Excel(name = "变更后积分")
private BigDecimal resultAmount; private BigDecimal resultAmount;
/** /**

View File

@ -52,12 +52,12 @@ public class AiGroupBalanceChangeRecord implements Serializable {
@Excel(name = "操作类型", readConverterExp = "0=充值,1=退款,2=下发,3=回收,4=手动修改") @Excel(name = "操作类型", readConverterExp = "0=充值,1=退款,2=下发,3=回收,4=手动修改")
private Integer type; private Integer type;
/** 变更金额 */ /** 变更积分 */
@Excel(name = "变更金额") @Excel(name = "变更积分")
private BigDecimal changeAmount; private BigDecimal changeAmount;
/** 变更后金额 */ /** 变更后金额 */
@Excel(name = "变更后金额") @Excel(name = "变更后积分")
private BigDecimal resultAmount; private BigDecimal resultAmount;
/** 备注(目前只作为手工修改时的备注) */ /** 备注(目前只作为手工修改时的备注) */

View File

@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.common.annotation.Excel; import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity; import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal; import java.math.BigDecimal;
/** /**
@ -15,6 +16,7 @@ import java.math.BigDecimal;
* @author shi * @author shi
* @date 2025-11-13 * @date 2025-11-13
*/ */
@EqualsAndHashCode(callSuper = true)
@Data @Data
@TableName("ai_order") @TableName("ai_order")
public class AiOrder extends BaseEntity { public class AiOrder extends BaseEntity {

View File

@ -37,10 +37,6 @@ public class AiVideoReportData implements Serializable {
@Excel(name = "部门ID") @Excel(name = "部门ID")
private Long deptId; private Long deptId;
/** 用户ID用户表主键 */
@Excel(name = "用户ID")
private Long userId;
/** 消耗积分,按任务创建时间统计 */ /** 消耗积分,按任务创建时间统计 */
@Excel(name = "消耗积分") @Excel(name = "消耗积分")
private BigDecimal score; private BigDecimal score;
@ -69,7 +65,7 @@ public class AiVideoReportData implements Serializable {
@TableField(exist = false) @TableField(exist = false)
private String deptName; private String deptName;
/** 查询日期 yyyyMMdd非表字段 */ /** 查询日期 yyyyMMdd非表字段,子团队单日统计等仍可用 */
@TableField(exist = false) @TableField(exist = false)
private String statDate; private String statDate;
} }

View File

@ -0,0 +1,22 @@
package com.ruoyi.ai.domain.request;
import java.io.Serializable;
import lombok.Data;
/**
* 管理端团队消耗统计列表/导出查询条件非表实体
*/
@Data
public class GroupReportDataRequest implements Serializable {
private static final long serialVersionUID = 1L;
/** 统计开始日 yyyy-MM-dd */
private String startDate;
/** 统计结束日 yyyy-MM-dd */
private String endDate;
/** 二级团队部门 ID可选为空表示不限团队 */
private Long deptId;
}

View File

@ -6,6 +6,7 @@ import java.math.BigDecimal;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.ai.domain.AiOrder; import com.ruoyi.ai.domain.AiOrder;
import com.ruoyi.ai.domain.AiVideoReportData; import com.ruoyi.ai.domain.AiVideoReportData;
import com.ruoyi.ai.domain.request.GroupReportDataRequest;
import com.ruoyi.system.domain.subteam.SubteamVideoMetrics; import com.ruoyi.system.domain.subteam.SubteamVideoMetrics;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@ -17,14 +18,11 @@ import org.apache.ibatis.annotations.Param;
*/ */
public interface AiVideoReportDataMapper extends BaseMapper<AiVideoReportData> { public interface AiVideoReportDataMapper extends BaseMapper<AiVideoReportData> {
/** /**
* 团队每日消耗统计查询团队聚合 * 团队每日消耗统计查询 date_key 8 团队聚合区间内多日均返回一行一天一团队
* *
* @param statDate 统计日期yyyyMMdd * @param query 查询条件startDate/endDate yyyy-MM-dd yyyyMMdd可选 deptId
* @param deptId 团队部门ID精确匹配
* @return 聚合结果
*/ */
List<AiVideoReportData> selectTeamDailyConsumeList(@Param("statDate") String statDate, List<AiVideoReportData> selectTeamDailyConsumeList(GroupReportDataRequest query);
@Param("deptId") Long deptId);
/** /**
* 按团队部门日期聚合团队后台消耗统计 * 按团队部门日期聚合团队后台消耗统计
@ -39,35 +37,5 @@ public interface AiVideoReportDataMapper extends BaseMapper<AiVideoReportData> {
@Param("startDay") String startDay, @Param("startDay") String startDay,
@Param("endDay") String endDay); @Param("endDay") String endDay);
/** void insertOrUpdateData(AiVideoReportData record);
* date_key, dept_id, user_id聚合累加视频消耗统计
*
* @param dateKey 小时KeyyyyyMMddHH
* @param deptId 部门ID
* @param userId 用户ID
* @param score 消耗积分增量
* @param orderCount 订单数增量
* @param useTokens 三方tokens增量
* @return 影响行数
*/
int upsertVideoConsumeIncrement(@Param("dateKey") String dateKey,
@Param("deptId") Long deptId,
@Param("userId") Long userId,
@Param("score") BigDecimal score,
@Param("orderCount") Long orderCount,
@Param("useTokens") Long useTokens);
/**
* date_key, dept_id, user_id=0聚合累加充值积分统计
*
* @param dateKey 小时KeyyyyyMMddHH
* @param deptId 部门ID
* @param rechargeScore 充值积分增量
* @return 影响行数
*/
int upsertRechargeScoreIncrement(@Param("dateKey") String dateKey,
@Param("deptId") Long deptId,
@Param("rechargeScore") BigDecimal rechargeScore);
void addNewOrderReportData(AiOrder aiOrder);
} }

View File

@ -29,14 +29,6 @@ public interface IAiChargeRefundOrderService {
*/ */
List<AiChargeRefundOrder> selectAiChargeRefundOrderList(AiChargeRefundOrder aiChargeRefundOrder); List<AiChargeRefundOrder> selectAiChargeRefundOrderList(AiChargeRefundOrder aiChargeRefundOrder);
/**
* 分页查询团队部门充值退款订单列表
*
* @param aiChargeRefundOrder 团队部门充值退款订单
* @return 团队部门充值退款订单集合
*/
IPage<AiChargeRefundOrder> selectAiChargeRefundOrderPage(Page page, AiChargeRefundOrder aiChargeRefundOrder);
/** /**
* 新增团队部门充值退款订单 * 新增团队部门充值退款订单
* *

View File

@ -3,9 +3,13 @@ package com.ruoyi.ai.service;
import java.util.List; import java.util.List;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import com.ruoyi.ai.domain.AiChargeRefundOrder;
import com.ruoyi.ai.domain.AiOrder;
import com.ruoyi.ai.domain.AiVideoReportData; import com.ruoyi.ai.domain.AiVideoReportData;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.ai.domain.request.GroupReportDataRequest;
/** /**
* AI视频生成统计数据作为其他统计报的数据源Service接口 * AI视频生成统计数据作为其他统计报的数据源Service接口
@ -24,60 +28,16 @@ public interface IAiVideoReportDataService {
AiVideoReportData selectAiVideoReportDataById(Long id); AiVideoReportData selectAiVideoReportDataById(Long id);
/** /**
* 查询AI视频生成统计数据作为其他统计报的数据源列表 * 团队每日消耗统计查询按日团队聚合支持日期区间与可选团队
*
* @param aiVideoReportData AI视频生成统计数据作为其他统计报的数据源
* @return AI视频生成统计数据作为其他统计报的数据源集合
*/ */
List<AiVideoReportData> selectAiVideoReportDataList(AiVideoReportData aiVideoReportData); List<AiVideoReportData> selectTeamDailyConsumeList(GroupReportDataRequest query);
/**
* 分页查询AI视频生成统计数据作为其他统计报的数据源列表
*
* @param aiVideoReportData AI视频生成统计数据作为其他统计报的数据源
* @return AI视频生成统计数据作为其他统计报的数据源集合
*/
IPage<AiVideoReportData> selectAiVideoReportDataPage(Page page, AiVideoReportData aiVideoReportData);
/**
* 新增AI视频生成统计数据作为其他统计报的数据源
*
* @param aiVideoReportData AI视频生成统计数据作为其他统计报的数据源
* @return 结果
*/
int insertAiVideoReportData(AiVideoReportData aiVideoReportData);
/**
* 团队每日消耗统计查询按天团队聚合
*
* @param statDate 统计日期yyyyMMdd必填
* @param deptId 团队部门ID必填精确匹配
* @return 聚合后的统计数据
*/
List<AiVideoReportData> selectTeamDailyConsumeList(String statDate, Long deptId);
/** /**
* 团队每日消耗按部门 ID团队后台 * 团队每日消耗按部门 ID团队后台
*/ */
List<AiVideoReportData> selectTeamDailyConsumeByDeptId(String statDate, Long deptId); List<AiVideoReportData> selectTeamDailyConsumeByDeptId(String statDate, Long deptId);
/** void updateReportDataWhenInsertAiOrder(AiOrder order);
* 按视频订单成功结果回写统计来源表按小时部门账号聚合累加
*
* @param createTime 任务创建时间
* @param deptId 部门ID
* @param userId 账号ID
* @param score 消耗积分增量
* @param useTokens 三方tokens增量
*/
void syncVideoConsumeIncrement(Date createTime, Long deptId, Long userId, BigDecimal score, Long useTokens);
/** void updateReportDataWhenInsertChargeRefundOrder(AiChargeRefundOrder order);
* 按充值/退款成功结果回写统计来源表按小时部门聚合累加
*
* @param createTime 订单创建时间
* @param deptId 部门ID
* @param rechargeScore 实际充值积分增量充值为正退款为负
*/
void syncRechargeScoreIncrement(Date createTime, Long deptId, BigDecimal rechargeScore);
} }

View File

@ -1,13 +1,14 @@
package com.ruoyi.ai.service.impl; package com.ruoyi.ai.service.impl;
import java.util.List;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.ai.domain.AiChargeRefundOrder;
import com.ruoyi.ai.mapper.AiChargeRefundOrderMapper;
import com.ruoyi.ai.service.IAiChargeRefundOrderService;
import com.ruoyi.ai.service.IAiVideoReportDataService;
import com.ruoyi.common.enums.ChargeRefundOrderStatusType; import com.ruoyi.common.enums.ChargeRefundOrderStatusType;
import com.ruoyi.common.enums.ChargeRefundOrderType;
import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.SecurityUtils;
@ -15,10 +16,9 @@ import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils; import com.ruoyi.common.utils.uuid.IdUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.ruoyi.ai.mapper.AiChargeRefundOrderMapper;
import com.ruoyi.ai.domain.AiChargeRefundOrder; import java.math.BigDecimal;
import com.ruoyi.ai.service.IAiVideoReportDataService; import java.util.List;
import com.ruoyi.ai.service.IAiChargeRefundOrderService;
/** /**
* 团队部门充值退款订单Service业务层处理 * 团队部门充值退款订单Service业务层处理
@ -57,22 +57,6 @@ public class AiChargeRefundOrderServiceImpl implements IAiChargeRefundOrderServi
return aiChargeRefundOrderMapper.selectAiChargeRefundOrderList(aiChargeRefundOrder); return aiChargeRefundOrderMapper.selectAiChargeRefundOrderList(aiChargeRefundOrder);
} }
/**
* 分页查询团队部门充值退款订单列表
*
* @param aiChargeRefundOrder 团队部门充值退款订单
* @return 团队部门充值退款订单
*/
@Override
public IPage<AiChargeRefundOrder> selectAiChargeRefundOrderPage(Page page, AiChargeRefundOrder aiChargeRefundOrder) {
if (aiChargeRefundOrder != null) {
aiChargeRefundOrder.setDeptName(null);
}
LambdaQueryWrapper<AiChargeRefundOrder> query = Wrappers.lambdaQuery(aiChargeRefundOrder);
query.eq(AiChargeRefundOrder::getDelFlag, "0");
return aiChargeRefundOrderMapper.selectPage(page, query);
}
/** /**
* 新增团队部门充值退款订单 * 新增团队部门充值退款订单
* *
@ -81,7 +65,6 @@ public class AiChargeRefundOrderServiceImpl implements IAiChargeRefundOrderServi
*/ */
@Override @Override
public int insertAiChargeRefundOrder(AiChargeRefundOrder aiChargeRefundOrder) { public int insertAiChargeRefundOrder(AiChargeRefundOrder aiChargeRefundOrder) {
validateChargeAmount(aiChargeRefundOrder);
if (StringUtils.isEmpty(aiChargeRefundOrder.getOrderNum())) { if (StringUtils.isEmpty(aiChargeRefundOrder.getOrderNum())) {
aiChargeRefundOrder.setOrderNum("CR" + IdUtils.fastSimpleUUID()); aiChargeRefundOrder.setOrderNum("CR" + IdUtils.fastSimpleUUID());
} }
@ -92,47 +75,7 @@ public class AiChargeRefundOrderServiceImpl implements IAiChargeRefundOrderServi
aiChargeRefundOrder.setCreateBy(SecurityUtils.getUserId()); aiChargeRefundOrder.setCreateBy(SecurityUtils.getUserId());
aiChargeRefundOrder.setCreateTime(DateUtils.getNowDate()); aiChargeRefundOrder.setCreateTime(DateUtils.getNowDate());
int rows = aiChargeRefundOrderMapper.insert(aiChargeRefundOrder); int rows = aiChargeRefundOrderMapper.insert(aiChargeRefundOrder);
syncRechargeReportData(aiChargeRefundOrder); aiVideoReportDataService.updateReportDataWhenInsertChargeRefundOrder(aiChargeRefundOrder);
return rows; return rows;
} }
/**
* 充值/退款单完成后订单创建时间同步到团队统计来源表
*
* @param order 充值退款订单
*/
private void syncRechargeReportData(AiChargeRefundOrder order) {
if (order == null || order.getCreateTime() == null || order.getDeptId() == null) {
return;
}
if (order.getStatus() == null || order.getStatus() != ChargeRefundOrderStatusType.SUCCESS.getCode()) {
return;
}
if (order.getOrderType() == null || order.getAmount() == null) {
return;
}
BigDecimal rechargeScore;
int type = order.getOrderType().intValue();
if (type == ChargeRefundOrderType.REFUND.getCode()) {
rechargeScore = order.getAmount().negate();
} else {
rechargeScore = order.getAmount();
}
aiVideoReportDataService.syncRechargeScoreIncrement(order.getCreateTime(), order.getDeptId(), rechargeScore);
}
/**
* 充值退款积分须为非负数
*/
private void validateChargeAmount(AiChargeRefundOrder o) {
if (o.getOrderType() == null || o.getAmount() == null) {
return;
}
int t = o.getOrderType().intValue();
if (t == ChargeRefundOrderType.CHARGE.getCode() || t == ChargeRefundOrderType.REFUND.getCode()) {
if (o.getAmount().compareTo(BigDecimal.ZERO) < 0) {
throw new ServiceException("充值、退款类型的积分须填写非负数");
}
}
}
} }

View File

@ -8,7 +8,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.ai.domain.AiManager; import com.ruoyi.ai.domain.AiManager;
import com.ruoyi.ai.domain.AiOrder; import com.ruoyi.ai.domain.AiOrder;
import com.ruoyi.ai.domain.AiStatistics; import com.ruoyi.ai.domain.AiStatistics;
import com.ruoyi.ai.domain.AiVideoReportData;
import com.ruoyi.ai.mapper.AiOrderMapper; import com.ruoyi.ai.mapper.AiOrderMapper;
import com.ruoyi.ai.mapper.AiVideoReportDataMapper; import com.ruoyi.ai.mapper.AiVideoReportDataMapper;
import com.ruoyi.ai.service.*; import com.ruoyi.ai.service.*;
@ -33,7 +32,6 @@ import java.math.BigDecimal;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.UUID; import java.util.UUID;
/** /**
@ -251,7 +249,6 @@ public class AiOrderServiceImpl implements IAiOrderService {
public void orderSuccess(AiOrder aiOrder) { public void orderSuccess(AiOrder aiOrder) {
aiOrder.setStatus(1); aiOrder.setStatus(1);
aiOrderMapper.updateById(aiOrder); aiOrderMapper.updateById(aiOrder);
syncVideoReportData(aiOrder);
AiStatistics aiStatistics = new AiStatistics(); AiStatistics aiStatistics = new AiStatistics();
aiStatistics.setSource(aiOrder.getSource()); aiStatistics.setSource(aiOrder.getSource());
aiStatistics.setGenerateCount(1L); aiStatistics.setGenerateCount(1L);
@ -259,7 +256,7 @@ public class AiOrderServiceImpl implements IAiOrderService {
aiStatisticsService.saveOrUpdateData(aiStatistics); aiStatisticsService.saveOrUpdateData(aiStatistics);
// 统计数据 // 统计数据
aiVideoReportDataMapper.addNewOrderReportData(aiOrder); aiVideoReportDataService.updateReportDataWhenInsertAiOrder(aiOrder);
} }
@Override @Override
@ -360,18 +357,4 @@ public class AiOrderServiceImpl implements IAiOrderService {
// BalanceChangerConstants.QUICK_VIDEO_GENERATION, TASK_SUCCESS_BALANCE_REMARK); // BalanceChangerConstants.QUICK_VIDEO_GENERATION, TASK_SUCCESS_BALANCE_REMARK);
return AjaxResult.success("callback success"); return AjaxResult.success("callback success");
} }
/**
* 任务成功后按提交时间写入团队消耗统计来源表
*
* @param aiOrder 已成功订单
*/
private void syncVideoReportData(AiOrder aiOrder) {
if (aiOrder == null || aiOrder.getCreateTime() == null || aiOrder.getDeptId() == null || aiOrder.getUserId() == null) {
return;
}
BigDecimal score = aiOrder.getAmount() != null ? aiOrder.getAmount() : BigDecimal.ZERO;
Long useTokens = aiOrder.getTotalUsage() != null ? aiOrder.getTotalUsage().longValue() : 0L;
aiVideoReportDataService.syncVideoConsumeIncrement(aiOrder.getCreateTime(), aiOrder.getDeptId(), aiOrder.getUserId(), score, useTokens);
}
} }

View File

@ -1,14 +1,20 @@
package com.ruoyi.ai.service.impl; package com.ruoyi.ai.service.impl;
import java.util.List;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.util.Date; import java.util.Date;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import java.util.List;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.annotation.TableField;
import com.ruoyi.common.utils.DateUtils; import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.ai.domain.AiChargeRefundOrder;
import com.ruoyi.ai.domain.AiOrder;
import com.ruoyi.ai.domain.request.GroupReportDataRequest;
import com.ruoyi.common.annotation.Excel;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.ruoyi.ai.mapper.AiVideoReportDataMapper; import com.ruoyi.ai.mapper.AiVideoReportDataMapper;
@ -38,46 +44,9 @@ public class AiVideoReportDataServiceImpl implements IAiVideoReportDataService {
return aiVideoReportDataMapper.selectById(id); return aiVideoReportDataMapper.selectById(id);
} }
/**
* 查询AI视频生成统计数据作为其他统计报的数据源列表
*
* @param aiVideoReportData AI视频生成统计数据作为其他统计报的数据源
* @return AI视频生成统计数据作为其他统计报的数据源
*/
@Override @Override
public List<AiVideoReportData> selectAiVideoReportDataList(AiVideoReportData aiVideoReportData) { public List<AiVideoReportData> selectTeamDailyConsumeList(GroupReportDataRequest query) {
LambdaQueryWrapper<AiVideoReportData> query = Wrappers.lambdaQuery(aiVideoReportData); return aiVideoReportDataMapper.selectTeamDailyConsumeList(query);
query.orderByDesc(AiVideoReportData::getId);
return aiVideoReportDataMapper.selectList(query);
}
/**
* 分页查询AI视频生成统计数据作为其他统计报的数据源列表
*
* @param aiVideoReportData AI视频生成统计数据作为其他统计报的数据源
* @return AI视频生成统计数据作为其他统计报的数据源
*/
@Override
public IPage<AiVideoReportData> selectAiVideoReportDataPage(Page page, AiVideoReportData aiVideoReportData) {
LambdaQueryWrapper<AiVideoReportData> query = Wrappers.lambdaQuery(aiVideoReportData);
return aiVideoReportDataMapper.selectPage(page, query);
}
/**
* 新增AI视频生成统计数据作为其他统计报的数据源
*
* @param aiVideoReportData AI视频生成统计数据作为其他统计报的数据源
* @return 结果
*/
@Override
public int insertAiVideoReportData(AiVideoReportData aiVideoReportData) {
aiVideoReportData.setCreateTime(DateUtils.getNowDate());
return aiVideoReportDataMapper.insert(aiVideoReportData);
}
@Override
public List<AiVideoReportData> selectTeamDailyConsumeList(String statDate, Long deptId) {
return aiVideoReportDataMapper.selectTeamDailyConsumeList(statDate, deptId);
} }
@Override @Override
@ -86,20 +55,24 @@ public class AiVideoReportDataServiceImpl implements IAiVideoReportDataService {
} }
@Override @Override
public void syncVideoConsumeIncrement(Date createTime, Long deptId, Long userId, BigDecimal score, Long useTokens) { public void updateReportDataWhenInsertAiOrder(AiOrder order) {
if (createTime == null || deptId == null || userId == null || score == null || useTokens == null) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH");
return; AiVideoReportData record = new AiVideoReportData();
} record.setDateKey(sdf.format(order.getCreateTime()));
String dateKey = new SimpleDateFormat("yyyyMMddHH").format(createTime); record.setDeptId(order.getDeptId());
aiVideoReportDataMapper.upsertVideoConsumeIncrement(dateKey, deptId, userId, score, 1L, useTokens); record.setScore(order.getAmount());
record.setOrderCount(1L);
record.setUseTokens(order.getTotalUsage().longValue());
aiVideoReportDataMapper.insertOrUpdateData(record);
} }
@Override @Override
public void syncRechargeScoreIncrement(Date createTime, Long deptId, BigDecimal rechargeScore) { public void updateReportDataWhenInsertChargeRefundOrder(AiChargeRefundOrder order) {
if (createTime == null || deptId == null || rechargeScore == null) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH");
return; AiVideoReportData record = new AiVideoReportData();
} record.setDateKey(sdf.format(order.getCreateTime()));
String dateKey = new SimpleDateFormat("yyyyMMddHH").format(createTime); record.setDeptId(order.getDeptId());
aiVideoReportDataMapper.upsertRechargeScoreIncrement(dateKey, deptId, rechargeScore); record.setRechargeScore(order.getAmount());
aiVideoReportDataMapper.insertOrUpdateData(record);
} }
} }

View File

@ -50,9 +50,10 @@ public class DeptChargeRefundServiceImpl implements IDeptChargeRefundService {
if (orderType == null) { if (orderType == null) {
throw new ServiceException("订单类型不合法"); throw new ServiceException("订单类型不合法");
} }
if (request.getAmount() <= 0) {
throw new ServiceException("积分须是大于0的正整数");
}
// 积分变动量 sys_dept.balance 增减一致money 仅落库订单不参与此处运算
BigDecimal amountBd = BigDecimal.valueOf(request.getAmount());
String orderNum = buildOrderNum(); String orderNum = buildOrderNum();
Date now = DateUtils.getNowDate(); Date now = DateUtils.getNowDate();
@ -65,9 +66,17 @@ public class DeptChargeRefundServiceImpl implements IDeptChargeRefundService {
order.setOrderNum(orderNum); order.setOrderNum(orderNum);
order.setDeptId(request.getDeptId()); order.setDeptId(request.getDeptId());
order.setOrderType(request.getOrderType()); order.setOrderType(request.getOrderType());
// 金额两位小数向零截断非四舍五入避免入账金额被抬高 // 金额两位小数
order.setMoney(request.getMoney().setScale(2, RoundingMode.DOWN)); BigDecimal money = request.getMoney().setScale(2, RoundingMode.DOWN);
order.setAmount(amountBd); // 积分变动量
BigDecimal amount = BigDecimal.valueOf(request.getAmount());
// 退款变小数
if (order.getOrderType() == ChargeRefundOrderType.REFUND.getCode()) {
money = money.negate();
amount = amount.negate();
}
order.setMoney(money);
order.setAmount(amount);
order.setRemark(request.getRemark()); order.setRemark(request.getRemark());
order.setStatus(ChargeRefundOrderStatusType.SUCCESS.getCode()); order.setStatus(ChargeRefundOrderStatusType.SUCCESS.getCode());
@ -76,9 +85,9 @@ public class DeptChargeRefundServiceImpl implements IDeptChargeRefundService {
// 2) 原子更新部门积分退款时 rows==0 表示余额不足或部门无效依赖事务回滚撤销上一 INSERT // 2) 原子更新部门积分退款时 rows==0 表示余额不足或部门无效依赖事务回滚撤销上一 INSERT
int rows; int rows;
if (orderType == ChargeRefundOrderType.CHARGE) { if (orderType == ChargeRefundOrderType.CHARGE) {
rows = deptService.addDeptBalance(request.getDeptId(), amountBd); rows = deptService.addDeptBalance(request.getDeptId(), amount.abs());
} else { } else {
rows = deptService.subtractDeptBalance(request.getDeptId(), amountBd); rows = deptService.subtractDeptBalance(request.getDeptId(), amount.abs());
} }
if (rows == 0) { if (rows == 0) {
if (orderType == ChargeRefundOrderType.REFUND) { if (orderType == ChargeRefundOrderType.REFUND) {
@ -93,17 +102,13 @@ public class DeptChargeRefundServiceImpl implements IDeptChargeRefundService {
} }
// 3) 流水充值为正退款为负result_amount 为变动后部门余额 // 3) 流水充值为正退款为负result_amount 为变动后部门余额
BigDecimal signedChange = orderType == ChargeRefundOrderType.CHARGE
? amountBd
: amountBd.negate();
AiGroupBalanceChangeRecord record = new AiGroupBalanceChangeRecord(); AiGroupBalanceChangeRecord record = new AiGroupBalanceChangeRecord();
record.setRelationOrderNo(orderNum); record.setRelationOrderNo(orderNum);
record.setDeptId(request.getDeptId()); record.setDeptId(request.getDeptId());
record.setType(orderType == ChargeRefundOrderType.CHARGE record.setType(orderType == ChargeRefundOrderType.CHARGE
? GroupBalanceChangeType.RECHARGE.getCode() ? GroupBalanceChangeType.RECHARGE.getCode()
: GroupBalanceChangeType.REFUND.getCode()); : GroupBalanceChangeType.REFUND.getCode());
record.setChangeAmount(signedChange); record.setChangeAmount(amount);
record.setResultAmount(dept.getBalance()); record.setResultAmount(dept.getBalance());
record.setRemark(request.getRemark()); record.setRemark(request.getRemark());
record.setCreateTime(now); record.setCreateTime(now);

View File

@ -8,7 +8,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="id" column="id" /> <result property="id" column="id" />
<result property="dateKey" column="date_key" /> <result property="dateKey" column="date_key" />
<result property="deptId" column="dept_id" /> <result property="deptId" column="dept_id" />
<result property="userId" column="user_id" />
<result property="score" column="score" /> <result property="score" column="score" />
<result property="orderCount" column="order_count" /> <result property="orderCount" column="order_count" />
<result property="useTokens" column="use_tokens" /> <result property="useTokens" column="use_tokens" />
@ -19,79 +18,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap> </resultMap>
<sql id="selectAiVideoReportDataVo"> <sql id="selectAiVideoReportDataVo">
select id, date_key, dept_id, user_id, score, recharge_score, order_count, use_tokens, create_time, update_time from ai_video_report_data select id, date_key, dept_id, score, recharge_score, order_count, use_tokens, create_time, update_time from ai_video_report_data
</sql> </sql>
<select id="selectAiVideoReportDataList" parameterType="AiVideoReportData" resultMap="AiVideoReportDataResult">
<include refid="selectAiVideoReportDataVo"/>
<where>
<if test="dateKey != null and dateKey != ''"> and date_key = #{dateKey}</if>
<if test="deptId != null "> and dept_id = #{deptId}</if>
<if test="userId != null "> and user_id = #{userId}</if>
<if test="score != null "> and score = #{score}</if>
<if test="orderCount != null "> and order_count = #{orderCount}</if>
<if test="useTokens != null "> and use_tokens = #{useTokens}</if>
<if test="rechargeScore != null "> and recharge_score = #{rechargeScore}</if>
</where>
</select>
<select id="selectAiVideoReportDataById" parameterType="Long" resultMap="AiVideoReportDataResult"> <select id="selectAiVideoReportDataById" parameterType="Long" resultMap="AiVideoReportDataResult">
<include refid="selectAiVideoReportDataVo"/> <include refid="selectAiVideoReportDataVo"/>
where id = #{id} where id = #{id}
</select> </select>
<insert id="insertAiVideoReportData" parameterType="AiVideoReportData" useGeneratedKeys="true" keyProperty="id"> <select id="selectTeamDailyConsumeList" parameterType="com.ruoyi.ai.domain.request.GroupReportDataRequest" resultMap="AiVideoReportDataResult">
insert into ai_video_report_data select substr(vrd.date_key, 1, 10) as date_key, vrd.dept_id, d.dept_name, sum(vrd.recharge_score) as recharge_score,
<trim prefix="(" suffix=")" suffixOverrides=","> sum(vrd.score) as score, sum(vrd.order_count) as order_count, sum(vrd.use_tokens) as use_tokens
<if test="dateKey != null and dateKey != ''">date_key,</if> from ai_video_report_data vrd left join sys_dept d on d.dept_id = vrd.dept_id
<if test="deptId != null">dept_id,</if>
<if test="userId != null">user_id,</if>
<if test="score != null">score,</if>
<if test="orderCount != null">order_count,</if>
<if test="useTokens != null">use_tokens,</if>
<if test="rechargeScore != null">recharge_score,</if>
<if test="createTime != null">create_time,</if>
<if test="updateTime != null">update_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="dateKey != null and dateKey != ''">#{dateKey},</if>
<if test="deptId != null">#{deptId},</if>
<if test="userId != null">#{userId},</if>
<if test="score != null">#{score},</if>
<if test="orderCount != null">#{orderCount},</if>
<if test="useTokens != null">#{useTokens},</if>
<if test="rechargeScore != null">#{rechargeScore},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateTime != null">#{updateTime},</if>
</trim>
</insert>
<select id="selectTeamDailyConsumeList" resultMap="AiVideoReportDataResult">
select
concat(substr(substr(vrd.date_key, 1, 8), 1, 4), '-', substr(substr(vrd.date_key, 1, 8), 5, 2), '-', substr(substr(vrd.date_key, 1, 8), 7, 2)) as date_key,
vrd.dept_id,
d.dept_name,
sum(vrd.recharge_score) as recharge_score,
sum(vrd.score) as score,
sum(vrd.order_count) as order_count,
sum(vrd.use_tokens) as use_tokens
from ai_video_report_data vrd
left join sys_dept d on d.dept_id = vrd.dept_id
<where> <where>
<if test="statDate != null and statDate != ''"> <if test="startDate != null and startDate != ''">
and vrd.date_key like concat(#{statDate}, '%') and substr(vrd.date_key, 1, 10) &gt;= #{startDate}
</if>
<if test="endDate != null and endDate != ''">
and substr(vrd.date_key, 1, 10) &lt;= #{endDate}
</if> </if>
<if test="deptId != null"> <if test="deptId != null">
and vrd.dept_id = #{deptId} and vrd.dept_id = #{deptId}
</if> </if>
</where> </where>
group by substr(vrd.date_key, 1, 8), vrd.dept_id, d.dept_name group by substr(vrd.date_key, 1, 10), vrd.dept_id, d.dept_name
order by substr(vrd.date_key, 1, 8) desc, vrd.dept_id desc order by substr(vrd.date_key, 1, 10) desc, vrd.dept_id desc
</select> </select>
<select id="selectTeamDailyConsumeByDeptId" resultMap="AiVideoReportDataResult"> <select id="selectTeamDailyConsumeByDeptId" resultMap="AiVideoReportDataResult">
select select substr(vrd.date_key, 1, 10) as date_key,
concat(substr(vrd.date_key, 1, 4), '-', substr(vrd.date_key, 5, 2), '-', substr(vrd.date_key, 7, 2)) as date_key,
vrd.dept_id, vrd.dept_id,
d.dept_name, d.dept_name,
sum(vrd.recharge_score) as recharge_score, sum(vrd.recharge_score) as recharge_score,
@ -102,8 +57,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
left join sys_dept d on d.dept_id = vrd.dept_id left join sys_dept d on d.dept_id = vrd.dept_id
where vrd.date_key like concat(#{statDate}, '%') where vrd.date_key like concat(#{statDate}, '%')
and vrd.dept_id = #{deptId} and vrd.dept_id = #{deptId}
group by substr(vrd.date_key, 1, 8), vrd.dept_id, d.dept_name group by substr(vrd.date_key, 1, 10), vrd.dept_id, d.dept_name
order by substr(vrd.date_key, 1, 8) desc, vrd.dept_id desc order by substr(vrd.date_key, 1, 10) desc, vrd.dept_id desc
</select> </select>
<select id="selectDeptVideoMetricsBetween" resultType="com.ruoyi.system.domain.subteam.SubteamVideoMetrics"> <select id="selectDeptVideoMetricsBetween" resultType="com.ruoyi.system.domain.subteam.SubteamVideoMetrics">
@ -118,9 +73,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<insert id="upsertVideoConsumeIncrement"> <insert id="upsertVideoConsumeIncrement">
insert into ai_video_report_data insert into ai_video_report_data
(date_key, dept_id, user_id, score, order_count, use_tokens, recharge_score, create_time, update_time) (date_key, dept_id, score, order_count, use_tokens, recharge_score, create_time, update_time)
values values
(#{dateKey}, #{deptId}, #{userId}, #{score}, #{orderCount}, #{useTokens}, 0, now(), now()) (#{dateKey}, #{deptId}, #{score}, #{orderCount}, #{useTokens}, 0, now(), now())
on duplicate key update on duplicate key update
score = score + values(score), score = score + values(score),
order_count = order_count + values(order_count), order_count = order_count + values(order_count),
@ -130,7 +85,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<insert id="upsertRechargeScoreIncrement"> <insert id="upsertRechargeScoreIncrement">
insert into ai_video_report_data insert into ai_video_report_data
(date_key, dept_id, user_id, score, order_count, use_tokens, recharge_score, create_time, update_time) (date_key, dept_id, score, order_count, use_tokens, recharge_score, create_time, update_time)
values values
(#{dateKey}, #{deptId}, 0, 0, 0, 0, #{rechargeScore}, now(), now()) (#{dateKey}, #{deptId}, 0, 0, 0, 0, #{rechargeScore}, now(), now())
on duplicate key update on duplicate key update

View File

@ -5,23 +5,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<mapper namespace="com.ruoyi.ai.mapper.AiVideoReportDataMapper"> <mapper namespace="com.ruoyi.ai.mapper.AiVideoReportDataMapper">
<!-- <!--
按订单 create_time 聚合到小时yyyy-MM-dd HH+ dept_id + user_id 维表。 按订单 create_time 聚合到小时yyyy-MM-dd HH+ dept_id 维表。
不存在则插入,存在则累加 score、order_count、use_tokens单条语句InnoDB 原子)。 不存在则插入,存在则累加 score、order_count、use_tokens单条语句InnoDB 原子)。
--> -->
<insert id="addNewOrderReportData"> <insert id="insertOrUpdateData">
INSERT INTO ai_video_report_data (date_key, dept_id, user_id, score, order_count, use_tokens) INSERT INTO ai_video_report_data (date_key, dept_id, score, recharge_score, order_count, use_tokens)
VALUES ( VALUES (#{dateKey}, #{deptId},
DATE_FORMAT(#{createTime}, '%Y-%m-%d %H'), IFNULL(#{score}, 0), IFNULL(#{rechargeScore}, 0), IFNULL(#{orderCount}, 0), IFNULL(#{useTokens}, 0)
#{deptId},
#{userId},
IFNULL(#{amount}, 0),
1,
IFNULL(#{totalUsage}, 0)
) )
ON DUPLICATE KEY UPDATE ON DUPLICATE KEY UPDATE
score = score + VALUES(score), score = score + VALUES(score),
recharge_score = recharge_score + VALUES(recharge_score),
order_count = order_count + VALUES(order_count), order_count = order_count + VALUES(order_count),
use_tokens = use_tokens + VALUES(use_tokens) use_tokens = use_tokens + VALUES(use_tokens)
</insert> </insert>
</mapper> </mapper>

View File

@ -31,8 +31,8 @@ CREATE TABLE `ai_balance_change_record` (
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
`user_id` bigint NULL DEFAULT NULL COMMENT '用户ID', `user_id` bigint NULL DEFAULT NULL COMMENT '用户ID',
`type` tinyint(1) NULL DEFAULT NULL COMMENT '操作类型', `type` tinyint(1) NULL DEFAULT NULL COMMENT '操作类型',
`change_amount` decimal(10, 2) NULL DEFAULT NULL COMMENT '变更金额', `change_amount` decimal(10, 2) NULL DEFAULT NULL COMMENT '变更积分',
`result_amount` decimal(10, 2) NULL DEFAULT NULL COMMENT '变更后金额', `result_amount` decimal(10, 2) NULL DEFAULT NULL COMMENT '变更后积分',
PRIMARY KEY (`id`) USING BTREE PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1159 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '余额使用记录' ROW_FORMAT = DYNAMIC; ) ENGINE = InnoDB AUTO_INCREMENT = 1159 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '余额使用记录' ROW_FORMAT = DYNAMIC;

View File

@ -54,8 +54,8 @@ CREATE TABLE `ai_balance_change_record` (
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注', `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
`user_id` bigint NULL DEFAULT NULL COMMENT '用户ID', `user_id` bigint NULL DEFAULT NULL COMMENT '用户ID',
`type` tinyint(1) NULL DEFAULT NULL COMMENT '操作类型', `type` tinyint(1) NULL DEFAULT NULL COMMENT '操作类型',
`change_amount` decimal(10, 2) NULL DEFAULT NULL COMMENT '变更金额', `change_amount` decimal(10, 2) NULL DEFAULT NULL COMMENT '变更积分',
`result_amount` decimal(10, 2) NULL DEFAULT NULL COMMENT '变更后金额', `result_amount` decimal(10, 2) NULL DEFAULT NULL COMMENT '变更后积分',
PRIMARY KEY (`id`) USING BTREE, PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_order_no`(`order_no` ASC) USING BTREE INDEX `idx_order_no`(`order_no` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1176 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '余额使用记录' ROW_FORMAT = DYNAMIC; ) ENGINE = InnoDB AUTO_INCREMENT = 1176 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '余额使用记录' ROW_FORMAT = DYNAMIC;

View File

@ -36,8 +36,8 @@ CREATE TABLE `ai_group_balance_change_record` (
`relation_order_no` VARCHAR(64) NULL DEFAULT NULL COMMENT '关联(充值/退款)订单号' COLLATE 'utf8mb4_0900_ai_ci', `relation_order_no` VARCHAR(64) NULL DEFAULT NULL COMMENT '关联(充值/退款)订单号' COLLATE 'utf8mb4_0900_ai_ci',
`dept_id` BIGINT NOT NULL COMMENT '部门ID', `dept_id` BIGINT NOT NULL COMMENT '部门ID',
`type` TINYINT NOT NULL COMMENT '操作类型0-充值、1-退款、2-下发、3-回收、4-手动修改)', `type` TINYINT NOT NULL COMMENT '操作类型0-充值、1-退款、2-下发、3-回收、4-手动修改)',
`change_amount` DECIMAL(12,2) NOT NULL COMMENT '变更金额', `change_amount` DECIMAL(12,2) NOT NULL COMMENT '变更积分',
`result_amount` DECIMAL(14,2) NOT NULL COMMENT '变更后金额', `result_amount` DECIMAL(14,2) NOT NULL COMMENT '变更后积分',
`remark` VARCHAR(50) NULL DEFAULT NULL COMMENT '备注(目前只作为手工修改时的备注)' COLLATE 'utf8mb4_unicode_ci', `remark` VARCHAR(50) NULL DEFAULT NULL COMMENT '备注(目前只作为手工修改时的备注)' COLLATE 'utf8mb4_unicode_ci',
`create_by` BIGINT NULL DEFAULT NULL COMMENT '创建者', `create_by` BIGINT NULL DEFAULT NULL COMMENT '创建者',
`create_time` DATETIME NOT NULL DEFAULT (now()) COMMENT '创建时间', `create_time` DATETIME NOT NULL DEFAULT (now()) COMMENT '创建时间',