2ed5dfa6a1
iotlcm-app:小程序端 + 用户端服务。 - app/:uni-app 小程序前端(基于 unibest,MIT) - 微信 AppID → YOUR_WX_APPID,uni-app AppID → __UNI__YOURAPPID - 内网后端地址 → admin.iotlcm.example.com - 应用标题与 unibest 模板 README 重写(保留上游致谢与 MIT 声明) - server/:GoFrame 用户端服务(基于 XiuAdmin,Apache-2.0) - module xiuadmin → git.5iot.com/iotlcm-public/iotlcm-app(360 文件) - 支付回调地址 → app.iotlcm.example.com,商户名与应用名泛化 - 真实手机号 → 合法 11 位编造号(保留号段前 3 位 + 后 4 位) - 依赖号码尾部的断言同步更新 - 补 server/LICENSE(Apache-2.0) - 移除内部资产:CI 触发器 - 补根 README,LF 行尾 + .gitattributes 已知:TestSysDept_RefreshDeptAncestors 在源仓同一提交上同样失败,属既有缺陷。
344 lines
9.9 KiB
Go
344 lines
9.9 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
|
|
"git.5iot.com/iotlcm-public/iotlcm-app/internal/consts"
|
|
"git.5iot.com/iotlcm-public/iotlcm-app/internal/dao"
|
|
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model"
|
|
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model/entity"
|
|
"git.5iot.com/iotlcm-public/iotlcm-app/internal/service"
|
|
|
|
"github.com/gogf/gf/v2/database/gdb"
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
)
|
|
|
|
type sAppOrder struct{}
|
|
|
|
func NewAppOrder() *sAppOrder {
|
|
return &sAppOrder{}
|
|
}
|
|
|
|
func init() {
|
|
service.RegisterAppOrder(NewAppOrder())
|
|
}
|
|
|
|
func (s *sAppOrder) List(ctx context.Context, param *model.AppOrderListParam) (items []*model.AppOrderModel, total int, err error) {
|
|
if param == nil {
|
|
param = &model.AppOrderListParam{}
|
|
}
|
|
if param.Page <= 0 {
|
|
param.Page = 1
|
|
}
|
|
if param.PageSize <= 0 {
|
|
param.PageSize = 10
|
|
}
|
|
if param.PageSize > 100 {
|
|
param.PageSize = 100
|
|
}
|
|
|
|
user, err := appCurrentUser(ctx)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
mod := s.ownerOrderModel(ctx, user)
|
|
mod = s.applyOrderFilters(mod, param)
|
|
|
|
total, err = mod.Count()
|
|
if err != nil {
|
|
return nil, 0, gerror.Wrap(err, "查询订单数量失败")
|
|
}
|
|
if total == 0 {
|
|
return []*model.AppOrderModel{}, 0, nil
|
|
}
|
|
|
|
var rows []*entity.TOrder
|
|
if err = mod.
|
|
Page(param.Page, param.PageSize).
|
|
OrderDesc(dao.TOrder.Columns().CreatedAt).
|
|
OrderDesc(dao.TOrder.Columns().Id).
|
|
Scan(&rows); err != nil {
|
|
return nil, 0, gerror.Wrap(err, "查询订单列表失败")
|
|
}
|
|
|
|
orderSns := make([]string, 0, len(rows))
|
|
for _, row := range rows {
|
|
if row != nil && strings.TrimSpace(row.OrderSn) != "" {
|
|
orderSns = append(orderSns, row.OrderSn)
|
|
}
|
|
}
|
|
itemMap, err := s.orderItemMap(ctx, user.TenantId, orderSns)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
items = make([]*model.AppOrderModel, 0, len(rows))
|
|
for _, row := range rows {
|
|
items = append(items, s.mapOrder(ctx, row, itemMap[row.OrderSn], false))
|
|
}
|
|
return items, total, nil
|
|
}
|
|
|
|
func (s *sAppOrder) Detail(ctx context.Context, param *model.AppOrderDetailParam) (*model.AppOrderModel, error) {
|
|
if param == nil {
|
|
param = &model.AppOrderDetailParam{}
|
|
}
|
|
orderSn := strings.TrimSpace(param.OrderSn)
|
|
if orderSn == "" {
|
|
return nil, gerror.New("订单号不能为空")
|
|
}
|
|
|
|
user, err := appCurrentUser(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var order *entity.TOrder
|
|
if err = s.ownerOrderModel(ctx, user).
|
|
Where(dao.TOrder.Columns().OrderSn, orderSn).
|
|
Limit(1).
|
|
Scan(&order); err != nil {
|
|
return nil, gerror.Wrap(err, "查询订单详情失败")
|
|
}
|
|
if order == nil || order.Id == 0 {
|
|
return nil, gerror.New("订单不存在或无权查看")
|
|
}
|
|
|
|
itemMap, err := s.orderItemMap(ctx, user.TenantId, []string{order.OrderSn})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.mapOrder(ctx, order, itemMap[order.OrderSn], true), nil
|
|
}
|
|
|
|
func (s *sAppOrder) ownerOrderModel(ctx context.Context, user *entity.SysUser) *gdb.Model {
|
|
orderTable := dao.TOrder.Table()
|
|
mod := dao.TOrder.Ctx(ctx).
|
|
Where(orderTable+"."+dao.TOrder.Columns().TenantId, user.TenantId)
|
|
if appIsTerminalUser(ctx, user) {
|
|
return mod.Where(orderTable+"."+dao.TOrder.Columns().CreatedBy, user.UserId)
|
|
}
|
|
return appApplyEnterpriseDataScope(
|
|
ctx,
|
|
user,
|
|
mod,
|
|
orderTable+"."+dao.TOrder.Columns().CreatedDept,
|
|
orderTable+"."+dao.TOrder.Columns().CreatedBy,
|
|
func(legacyMod *gdb.Model) *gdb.Model {
|
|
return legacyMod.Where(orderTable+"."+dao.TOrder.Columns().CreatedBy, user.UserId)
|
|
},
|
|
)
|
|
}
|
|
|
|
func (s *sAppOrder) applyOrderFilters(mod *gdb.Model, param *model.AppOrderListParam) *gdb.Model {
|
|
if param.OrderType > 0 {
|
|
mod = mod.Where(dao.TOrder.Columns().OrderType, param.OrderType)
|
|
}
|
|
if param.PayStatus != nil && *param.PayStatus >= 0 {
|
|
mod = mod.Where(dao.TOrder.Columns().PayStatus, *param.PayStatus)
|
|
}
|
|
|
|
keyword := strings.TrimSpace(param.Keyword)
|
|
if keyword == "" {
|
|
keyword = strings.TrimSpace(param.PageInfo.Keyword)
|
|
}
|
|
if keyword != "" {
|
|
like := "%" + keyword + "%"
|
|
orderTable := dao.TOrder.Table()
|
|
itemTable := dao.TOrderItem.Table()
|
|
mod = mod.Where(
|
|
"("+orderTable+"."+dao.TOrder.Columns().OrderSn+" LIKE ? OR "+
|
|
orderTable+"."+dao.TOrder.Columns().PayOrderSn+" LIKE ? OR "+
|
|
orderTable+"."+dao.TOrder.Columns().Remark+" LIKE ? OR EXISTS (SELECT 1 FROM "+itemTable+" oi WHERE oi."+dao.TOrderItem.Columns().OrderSn+" = "+orderTable+"."+dao.TOrder.Columns().OrderSn+" AND oi."+dao.TOrderItem.Columns().ProductName+" LIKE ?))",
|
|
like, like, like, like,
|
|
)
|
|
}
|
|
return mod
|
|
}
|
|
|
|
func (s *sAppOrder) orderItemMap(ctx context.Context, tenantId string, orderSns []string) (map[string][]*entity.TOrderItem, error) {
|
|
result := make(map[string][]*entity.TOrderItem)
|
|
if len(orderSns) == 0 {
|
|
return result, nil
|
|
}
|
|
|
|
var rows []*entity.TOrderItem
|
|
if err := dao.TOrderItem.Ctx(ctx).
|
|
Where(dao.TOrderItem.Columns().TenantId, tenantId).
|
|
WhereIn(dao.TOrderItem.Columns().OrderSn, orderSns).
|
|
OrderAsc(dao.TOrderItem.Columns().ItemId).
|
|
Scan(&rows); err != nil {
|
|
return nil, gerror.Wrap(err, "查询订单项失败")
|
|
}
|
|
for _, row := range rows {
|
|
if row == nil {
|
|
continue
|
|
}
|
|
result[row.OrderSn] = append(result[row.OrderSn], row)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (s *sAppOrder) mapOrder(ctx context.Context, row *entity.TOrder, items []*entity.TOrderItem, includeItems bool) *model.AppOrderModel {
|
|
if row == nil {
|
|
return nil
|
|
}
|
|
|
|
mappedItems := make([]*model.AppOrderItemModel, 0, len(items))
|
|
for _, item := range items {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
mappedItems = append(mappedItems, &model.AppOrderItemModel{
|
|
ItemId: item.ItemId,
|
|
ProductName: item.ProductName,
|
|
ProductSn: item.ProductSn,
|
|
Price: appRoundAmount(item.Price),
|
|
PriceText: appFormatMoney(item.Price),
|
|
Quantity: item.Quantity,
|
|
OriginPrice: appRoundAmount(item.OriginPrice),
|
|
OriginPriceText: appFormatMoney(item.OriginPrice),
|
|
ProductType: item.ProductType,
|
|
ServiceBeginTime: item.ServiceBeginTime,
|
|
ServiceEndTime: item.ServiceEndTime,
|
|
})
|
|
}
|
|
|
|
itemSummary := "订单商品"
|
|
if len(mappedItems) > 0 && strings.TrimSpace(mappedItems[0].ProductName) != "" {
|
|
itemSummary = strings.TrimSpace(mappedItems[0].ProductName)
|
|
if len(mappedItems) > 1 {
|
|
itemSummary += " 等"
|
|
}
|
|
}
|
|
orderTypeText := appOrderTypeText(row.OrderType)
|
|
switch appOrderBusinessFromItems(items) {
|
|
case "miniapp_card_change_batch":
|
|
orderTypeText = "套餐变更"
|
|
case "miniapp_card_renew_batch":
|
|
orderTypeText = "套餐续费"
|
|
}
|
|
paymentExpireAt, paymentRemainingSeconds, paymentExpired := appBalanceRechargePaymentTiming(ctx, row)
|
|
|
|
res := &model.AppOrderModel{
|
|
OrderId: row.Id,
|
|
OrderSn: row.OrderSn,
|
|
OrderType: row.OrderType,
|
|
OrderTypeText: orderTypeText,
|
|
OrderStatus: row.OrderStatus,
|
|
OrderStatusText: appOrderStatusText(row.OrderStatus),
|
|
PayType: row.PayType,
|
|
PayTypeText: appOrderPayTypeText(row.PayType),
|
|
PayStatus: row.PayStatus,
|
|
PayStatusText: appOrderPayStatusText(row.PayStatus),
|
|
PayOrderSn: row.PayOrderSn,
|
|
ReturnStatus: row.ReturnStatus,
|
|
ReturnStatusText: appOrderReturnStatusText(row.ReturnStatus),
|
|
PaymentExpireAt: paymentExpireAt,
|
|
PaymentRemainingSeconds: paymentRemainingSeconds,
|
|
PaymentExpired: paymentExpired,
|
|
TotalAmount: appRoundAmount(row.TotalAmount),
|
|
TotalAmountText: appFormatMoney(row.TotalAmount),
|
|
PaidAmount: appRoundAmount(row.PaidAmount),
|
|
PaidAmountText: appFormatMoney(row.PaidAmount),
|
|
OnlinePaidAmount: appRoundAmount(row.OnlinePaidAmount),
|
|
OnlinePaidAmountText: appFormatMoney(row.OnlinePaidAmount),
|
|
Balance: appRoundAmount(row.Balance),
|
|
BalanceText: appFormatMoney(row.Balance),
|
|
UnpaidAmount: appRoundAmount(row.UnpaidAmount),
|
|
UnpaidAmountText: appFormatMoney(row.UnpaidAmount),
|
|
UnrefundAmount: appRoundAmount(row.UnrefundAmount),
|
|
UnrefundAmountText: appFormatMoney(row.UnrefundAmount),
|
|
BuyerNote: row.BuyerNote,
|
|
Remark: row.Remark,
|
|
CreatedAt: row.CreatedAt,
|
|
PayTime: row.PayTime,
|
|
ItemSummary: itemSummary,
|
|
ItemCount: len(mappedItems),
|
|
}
|
|
if includeItems {
|
|
res.Items = mappedItems
|
|
}
|
|
return res
|
|
}
|
|
|
|
func appOrderBusinessFromItems(items []*entity.TOrderItem) string {
|
|
for _, item := range items {
|
|
if item == nil || strings.TrimSpace(item.ExtraSkuData) == "" {
|
|
continue
|
|
}
|
|
var extra struct {
|
|
Business string `json:"business"`
|
|
}
|
|
if err := json.Unmarshal([]byte(item.ExtraSkuData), &extra); err != nil {
|
|
continue
|
|
}
|
|
if strings.TrimSpace(extra.Business) != "" {
|
|
return strings.TrimSpace(extra.Business)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func appOrderTypeText(orderType int) string {
|
|
switch orderType {
|
|
case consts.OrderTypeCardSubscribe:
|
|
return "套餐续费"
|
|
case consts.OrderTypeBalanceRecharge:
|
|
return "余额充值"
|
|
default:
|
|
return "其他订单"
|
|
}
|
|
}
|
|
|
|
func appOrderStatusText(status int) string {
|
|
switch status {
|
|
case consts.OrderStatusPendingPay:
|
|
return "待支付"
|
|
case consts.OrderStatusCompleted:
|
|
return "已完成"
|
|
default:
|
|
return "处理中"
|
|
}
|
|
}
|
|
|
|
func appOrderPayTypeText(payType int) string {
|
|
switch payType {
|
|
case consts.PayTypeBalance:
|
|
return "余额"
|
|
case consts.PayTypeOnline:
|
|
return "线上支付"
|
|
default:
|
|
return "未支付"
|
|
}
|
|
}
|
|
|
|
func appOrderPayStatusText(payStatus int) string {
|
|
switch payStatus {
|
|
case consts.PayStatusUnpaid:
|
|
return "待支付"
|
|
case consts.PayStatusPaid:
|
|
return "已支付"
|
|
default:
|
|
return "处理中"
|
|
}
|
|
}
|
|
|
|
func appOrderReturnStatusText(status int) string {
|
|
switch status {
|
|
case consts.OrderReturnStatusPending:
|
|
return "退款申请中"
|
|
case consts.OrderReturnStatusRunning:
|
|
return "退款处理中"
|
|
case consts.OrderReturnStatusPartial:
|
|
return "退款异常"
|
|
case consts.OrderReturnStatusSuccess:
|
|
return "已退款"
|
|
default:
|
|
return "未退款"
|
|
}
|
|
}
|